[maven-release-plugin] copy for tag 0.4
git-svn-id: http://svn.apache.org/repos/asf/bval/tags/0.4@1311345 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/trunk/CHANGES.txt b/trunk/CHANGES.txt
new file mode 100644
index 0000000..56144d3
--- /dev/null
+++ b/trunk/CHANGES.txt
@@ -0,0 +1,214 @@
+Apache BVal (incubating) - CHANGES.txt
+Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
+--------------------------------------------------------------------------------
+
+Content
+=======
+ * Overview
+ * Disclaimer
+ * Prerequisites
+ * Documentation
+ * Getting Involved
+ * Release Notes
+
+
+Overview
+========
+This is an implementation of JSR 303 (Bean Validation), a specification of the
+Java API for Javabean validation in Java EE and Java SE.
+The technical objective is to provide a class level constraint declaration and
+validation facility for the Java application developer, as well as a constraint
+metadata repository and query API.
+
+This implementation is based on the validation framework of agimatec GmbH,
+which was contributed to the ASF under a software grant.
+
+
+Disclaimer
+==========
+Apache BVal is an effort undergoing incubation at The Apache Software
+Foundation (ASF), sponsored by the Apache Incubator PMC. Incubation is required
+of all newly accepted projects until a further review indicates that the
+infrastructure, communications, and decision making process have stabilized in a
+manner consistent with other successful ASF projects. While incubation status is
+not necessarily a reflection of the completeness or stability of the code, it
+does indicate that the project has yet to be fully endorsed by the ASF.
+
+
+Prerequisites
+=============
+You need a platform that supports Java SE 5 or later.
+
+
+Documentation
+=============
+Please refer to the project page at:
+ http://incubator.apache.org/bval/
+
+
+Getting Involved
+================
+The Apache BVal project is being built by the open source community
+for the open source community - we welcome your input and contributions!
+
+What are we looking for?
+ * Source code and fixes contributions
+ * Documentation assistance
+ * Project and feature suggestions
+ * Integration and testing with other projects
+ * Detailed and constructive feedback
+ * Articles and whitepapers
+
+How do I Contribute?
+ * To discuss BVal topics, check out the mailing lists.
+ * Bugs and other issues can be posted on the project JIRA.
+
+
+Release Notes - BVal - Version 0.4
+==================================
+
+Bug
+---
+ * [BVAL-54] - Handle Java security policies and managers
+ * [BVAL-63] - use java.util.ServiceLoader in DefaultValidationProviderResolver#getValidationProviders()
+ * [BVAL-89] - Application specific constraints and artifacts not visible by provider in OSGI-based app server
+ * [BVAL-90] - Inconsistent fraction handling of @Digits when target is a BigDecimal
+ * [BVAL-91] - Security hole in org.apache.bval.util.MethodAccess.get()
+ * [BVAL-92] - Security holes in org.apache.bval.util.PrivilegedActions
+ * [BVAL-94] - Missing SVN EOL properties
+ * [BVAL-95] - Beanvalidation with Generic-DataTypes
+ * [BVAL-96] - Use correct encoding for localization messages
+ * [BVAL-97] - More than one META-INF/validation.xml in tomcat webapp
+ * [BVAL-100] - Java2 Security AccessControlException after moving to use common-lang3
+ * [BVAL-104] - german validation messages properties are broken
+
+Improvement
+-----------
+ * [BVAL-68] - Use FindBugs to reduce coding errors
+
+Task
+----
+ * [BVAL-99] - upgrade to apache parent pom 10
+ * [BVAL-103] - switch BVal from slf4j to java.util.logging
+
+
+Release Notes for 0.3-incubating
+================================
+
+Bug
+---
+ * [BVAL-84] - CNFEX org.slf4j.impl.StaticLoggerBinder building xstream and guice
+ * [BVAL-86] - Missing DecimalMax/DecimalMin constraint messages in ValidationMessages.properties
+ * [BVAL-87] - Java 2 security violations in ClassValidator.validate
+ * [BVAL-88] - Cascaded validation adds a constraint violation for valid child property
+
+Improvement
+-----------
+ * [BVAL-60] - Use SLF4J API for logging
+ * [BVAL-85] - bval-core has no dependency on slf4j-jcl*
+
+
+Release Notes for 0.2-incubating
+================================
+
+Bug
+---
+ * [BVAL-47] - Correctly follow GroupSequence definitions when validating
+ * [BVAL-70] - Remove @Override annotations from methods implementing interfaces
+ * [BVAL-71] - Compile errors with Java SE 5
+ * [BVAL-72] - NPE when doing methodvalidation
+ * [BVAL-77] - Recent changes made the bval-xstream module required, instead of optional
+
+Improvement
+-----------
+ * [BVAL-59] - Write or reuse an scm-plugin mojo to capture the svn revision
+ * [BVAL-69] - make dependency to com.thoughtworks.xstream of bval-core optional or obsolete
+ * [BVAL-73] - Alternative way of addressing code reuse issue between the validators
+ * [BVAL-74] - Remove redundant method call from ClassValidator.validateProperty()
+ * [BVAL-75] - javadocs, compiler (generics) warnings, etc.
+ * [BVAL-76] - Correct spelling error in API: ConstraintValidationListener.getConstaintViolations()
+ * [BVAL-78] - Avoid unsafe casting and object swapping when processing @ReportAsSingleViolation composed constraints
+ * [BVAL-79] - email validation can easily work for any CharSequence rather than String only
+
+
+Release Notes for 0.1-incubating
+================================
+
+Sub-task
+--------
+ * [BVAL-3] - Move all copyrights to NOTICE files
+ * [BVAL-4] - Rename java packages
+ * [BVAL-5] - Update builds for incubator
+ * [BVAL-7] - Setup initial structure/navigation/content in the wiki
+ * [BVAL-8] - Setup confluence autoexport template
+ * [BVAL-9] - Setup rsync cron job to copy autoexport content over to www staging area
+
+Bug
+---
+ * [BVAL-11] - After moved java packages tests don't work
+ * [BVAL-13] - ConstraintViolationImpl must be Serialziable according to JSR303 spec
+ * [BVAL-15] - @Min and @Max fail with very high values of long
+ * [BVAL-17] - PathImpl keeps a null-named first node on unindexed property paths
+ * [BVAL-18] - DefaultMessageInterpolator substitutes unexistant variables with the whole message
+ * [BVAL-19] - Several changes to pass 12 more tests
+ * [BVAL-20] - When building a MetaBean, the full tree of subclasses/interfaces must be considered
+ * [BVAL-21] - ConstraintViolations set should not have duplicates and should store ElementType and use it in equals()
+ * [BVAL-22] - Incorrect output or IOBException when interpolating messages when annotation values have $ or \
+ * [BVAL-23] - Circular dependency check produces false positives
+ * [BVAL-24] - IllegalArgumentException should be thrown when calling Validator.getConstraintsForClass() with a null class
+ * [BVAL-26] - IllegalAccessException is thrown when accesing annotation values
+ * [BVAL-27] - IllegalArgumentException should be thrown when calling BeanDescriptor.getConstraintsForProperty(null)
+ * [BVAL-28] - Property path for nodes inside iterables that do not implement List should not contain the index
+ * [BVAL-29] - Defer node creation in implementation of ConstraintViolationBuilder API
+ * [BVAL-30] - Groups from the main constraint annotation must be inherited by the composing annotations
+ * [BVAL-31] - A ConstraintDefinitionException should be thrown when the resolved validator is not compatible with the annotation type
+ * [BVAL-32] - Annotations associated with composited contraints must reflect inherited groups
+ * [BVAL-33] - Payload value must also be inherited in compositing constraints
+ * [BVAL-34] - Parameter for @Pattern message should be {regexp}
+ * [BVAL-35] - Improved support of @OverridesAttribute
+ * [BVAL-36] - Improve @ReportAsSingleValidation implementation
+ * [BVAL-37] - Ensure constraint definitions are valid
+ * [BVAL-38] - Groups and payload values must be part of the ConstraintDescriptor attributes
+ * [BVAL-39] - Implicit groups should be correctly set in constraint descriptors
+ * [BVAL-41] - Package bval artifacts as bundles
+ * [BVAL-42] - Stop group sequence validation when at least 1 violation is already reported
+ * [BVAL-43] - GroupDefinitionException must be thrown on cyclic group detection
+ * [BVAL-45] - ValidationException must be thrown when validating a bean with an annotated method which isn't a valid getter
+ * [BVAL-48] - jUnit failure during build on Windows - trunk rev 945800
+ * [BVAL-49] - ValidationException must be thrown in case ConstraintValidatorFactory returns null for a valid getInstance call
+ * [BVAL-50] - Fix TraversableResolver usage
+ * [BVAL-51] - Several fixes in ElementDescriptor and subinterfaces impls
+ * [BVAL-53] - Several fixes for XML metadata processing impl
+ * [BVAL-55] - Fix remaining TCK tests
+ * [BVAL-58] - Clean up build before release
+ * [BVAL-62] - add missing ASL headers
+ * [BVAL-64] - org.apache.bval.bundle needs a custom NOTICE file
+ * [BVAL-65] - Incubator required DISCLAIMER missing from jars
+ * [BVAL-67] - Resolve NOTICE file issues fond during RC2 vote
+
+Improvement
+-----------
+ * [BVAL-12] - Current implementation of method validation (appendix C of JSR-303) doesn't work with some constraints
+ * [BVAL-16] - Maven project to launch TCK suite
+ * [BVAL-44] - Move optional JSON code out of bval-core to a bval-json module
+ * [BVAL-46] - Create project source assembly
+ * [BVAL-52] - ValidationParser.getInputStream() should call getResourceAsStream() for TCK suite to work
+ * [BVAL-57] - Create a single apache-bval jar/bundle
+ * [BVAL-66] - Method validation parameter processing problems
+
+New Feature
+-----------
+ * [BVAL-40] - Provide a way to display the BVal version being used
+ * [BVAL-56] - make bval work with geronimo spec osgi service locator
+
+Task
+----
+ * [BVAL-2] - Initial codebase donation tasks
+ * [BVAL-6] - Setup project website
+
+Test
+----
+ * [BVAL-10] - Setup continuous builds
+
+--------------------------------------------------------------------------------
+
diff --git a/trunk/LICENSE b/trunk/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/trunk/LICENSE
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
diff --git a/trunk/NOTICE b/trunk/NOTICE
new file mode 100644
index 0000000..28b288f
--- /dev/null
+++ b/trunk/NOTICE
@@ -0,0 +1,14 @@
+
+Apache Incubator - BVal project
+Copyright 2010-2012 The Apache Software Foundation.
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by Agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/README.txt b/trunk/README.txt
new file mode 100644
index 0000000..5047689
--- /dev/null
+++ b/trunk/README.txt
@@ -0,0 +1,66 @@
+Apache BVal - README.txt
+Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
+--------------------------------------------------------------------------------
+
+About
+=====
+This is an implementation of JSR 303 (Bean Validation), a specification of the
+Java API for Javabean validation in Java EE and Java SE.
+The technical objective is to provide a class level constraint declaration and
+validation facility for the Java application developer, as well as a constraint
+metadata repository and query API.
+
+
+Release Notes
+=============
+Please refer to the included CHANGES.txt for a complete list of new features
+available and issues resolved in this release.
+
+
+Getting Started
+===============
+Please visit the project website for the latest information:
+ http://bval.apache.org/
+
+Along with the developer mailing list archive:
+ http://mail-archives.apache.org/mod_mbox/bval-dev/
+
+
+System Requirements
+===================
+You need a platform that supports Java SE 5 or later.
+Most testing has been done with Java SE 6 on Mac OS X, Windows and Linux.
+
+
+Building
+==========================
+To build from source code:
+ - Sources require Java SE 5 or higher. (Tested with JDK 1.5.0_22 and 1.6.0_21)
+ - The project is built with Apache Maven (suggested is at least 2.2.1).
+ You need to download and install Maven from: http://maven.apache.org/
+ - Invoke maven in the root directory or a module subdirectory:
+ mvn install
+
+Checkout the Building section on the project web site for more details:
+ http://bval.apache.org/
+
+
+Support
+=======
+Any problems with this release can be reported to the user mailing list or
+opened in the Jira issue tracker.
+
+ Mailing list archive:
+ http://mail-archives.apache.org/mod_mbox/bval-user/
+
+ User mailing list:
+ user@bval.apache.org
+
+ User mailing list subscription:
+ user-subscribe@bval.apache.org
+
+ Jira:
+ https://issues.apache.org/jira/browse/BVAL/
+
+--------------------------------------------------------------------------------
+
diff --git a/trunk/bundle/pom.xml b/trunk/bundle/pom.xml
new file mode 100644
index 0000000..dd7a55b
--- /dev/null
+++ b/trunk/bundle/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <!-- use fully qualified naming for OSGi bundles -->
+ <artifactId>org.apache.bval.bundle</artifactId>
+ <name>Apache BVal :: org.apache.bval.bundle</name>
+ <packaging>bundle</packaging>
+
+ <description>Apache BVal - Aggregate OSGi Bundle (bval-core + bval-jsr303)</description>
+
+ <dependencies>
+ <!-- JARs to include in aggregate JAR via maven-shade-plugin -->
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-jsr303</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <!-- OSGi Bundle Metadata -->
+ <Main-Class>org.apache.bval.util.BValVersion</Main-Class>
+ <Implementation-Title>Apache BVal</Implementation-Title>
+ <Implementation-Build>${buildNumber}</Implementation-Build>
+ <Specification-Title>JSR-303 Bean Validation</Specification-Title>
+ <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
+ <Specification-Version>1.0</Specification-Version>
+ <Bundle-DocURL>${project.url}</Bundle-DocURL>
+ <Embed-Dependency>*;inline=true</Embed-Dependency>
+ <Private-Package />
+ <Export-Package>org.apache.bval.*</Export-Package>
+ <Import-Package>javax.persistence.*;resolution:=optional,org.apache.bval.xml.*;resolution:=optional,*</Import-Package>
+ <!-- geronimo spec osgi services support -->
+ <SPI-Provider>true</SPI-Provider>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>apache-release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <configuration>
+ <attach>false</attach>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
+
diff --git a/trunk/bundle/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bundle/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..05ddae6
--- /dev/null
+++ b/trunk/bundle/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,26 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-core/pom.xml b/trunk/bval-core/pom.xml
new file mode 100644
index 0000000..5251d67
--- /dev/null
+++ b/trunk/bval-core/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-core</artifactId>
+ <name>Apache BVal :: bval-core</name>
+ <packaging>jar</packaging>
+
+ <description>BVal Metadata Engine</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!--
+ get the svn revision number and project version
+ and set it in a properties file for later retrieval
+ -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>set subversion revision</id>
+ <phase>compile</phase>
+ <configuration>
+ <tasks>
+ <echo>Version: ${project.version}</echo>
+ <echo>Revision: ${buildNumber}</echo>
+ <echo>Date: ${timestamp}</echo>
+ <mkdir dir="${project.build.outputDirectory}/META-INF" />
+ <echo file="${project.build.outputDirectory}/META-INF/org.apache.bval.revision.properties">
+# Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
+project.name=Apache BVal
+project.version=${project.version}
+svn.revision=${buildNumber}
+svn.branch=${scmBranch}
+build.timestamp=${timestamp}
+ </echo>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- create mainClass attribute -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-jar</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ <mainClass>org.apache.bval.util.BValVersion</mainClass>
+ </manifest>
+ <manifestEntries>
+ <Implementation-Build>${buildNumber}</Implementation-Build>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </execution>
+ <execution>
+ <id>attach-tests</id>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>
+ org.apache.maven.plugins
+ </groupId>
+ <artifactId>
+ maven-antrun-plugin
+ </artifactId>
+ <versionRange>
+ [1.3,)
+ </versionRange>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+</project>
+
diff --git a/trunk/bval-core/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bval-core/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..9a0c1ee
--- /dev/null
+++ b/trunk/bval-core/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,25 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java
new file mode 100644
index 0000000..b9b8373
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import org.apache.bval.model.*;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.PropertyAccess;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * Description: Context during validation to help the {@link org.apache.bval.model.Validation}
+ * and the {@link BeanValidator} do their jobs.
+ * Used to bundle {@link BeanValidationContext} and {@link ValidationListener}
+ * together <br/>
+ * <b>This class is NOT thread-safe: a new instance will be created for each
+ * validation
+ * processing per thread.<br/></b>
+ */
+public class BeanValidationContext<T extends ValidationListener>
+ implements ValidationContext<T> {
+ /** represent an unknown propertyValue. */
+ private static final Object UNKNOWN = new Object() {
+ public String toString() {
+ return "unknown property value";
+ };
+ };
+
+ /** metainfo of current object. */
+ private MetaBean metaBean;
+ /** current object. */
+ private Object bean;
+ /** metainfo of current property. */
+ private MetaProperty metaProperty;
+ /**
+ * cached value of current property.
+ * Cached because of potential redundant access for different Validations
+ */
+ private Object propertyValue = UNKNOWN;
+
+ /** access strategy used for previous access */
+ private AccessStrategy access;
+
+ /** set of objects already validated to avoid endless loops. */
+ @SuppressWarnings({ "rawtypes" })
+ protected Map validatedObjects;
+
+ /**
+ * true when value is fixed, so that it will NOT be dynamically
+ * determined from the annotated element or the metaProperty.
+ * <b><br>Note: When value is UNKNOWN, it will be determined THE FIRST TIME
+ * IT IS ACCESSED.</b>
+ */
+ private boolean fixed;
+
+ /** listener notified of validation constraint violations. */
+ private T listener;
+
+ /**
+ * Create a new BeanValidationContext instance.
+ * @param listener
+ */
+ @SuppressWarnings({ "rawtypes" })
+ public BeanValidationContext(T listener) {
+ this(listener, new IdentityHashMap());
+ }
+
+ /**
+ * Create a new BeanValidationContext instance.
+ * @param listener
+ * @param validatedMap
+ */
+ @SuppressWarnings({ "rawtypes" })
+ protected BeanValidationContext(T listener, Map validatedMap) {
+ this.listener = listener;
+ this.validatedObjects = validatedMap;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T getListener() {
+ return listener;
+ }
+
+ /**
+ * Set the listener.
+ * @param listener T
+ */
+ public void setListener(T listener) {
+ this.listener = listener;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Here, state equates to a given bean reference.
+ */
+ @SuppressWarnings("unchecked")
+ public boolean collectValidated() {
+ return validatedObjects.put(getBean(), Boolean.TRUE) == null;
+ }
+
+ /**
+ * Learn whether a particular object has been validated.
+ * @param object
+ * @return true when the object has already been validated in this context
+ */
+ public boolean isValidated(Object object) {
+ return validatedObjects.containsKey(object);
+ }
+
+ /**
+ * Clear map of validated objects (invoke when you want to 'reuse' the
+ * context for different validations)
+ */
+ public void resetValidated() {
+ validatedObjects.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBean(Object aBean, MetaBean aMetaBean) {
+ bean = aBean;
+ metaBean = aMetaBean;
+ metaProperty = null;
+ unknownValue();
+ }
+
+ /**
+ * Get the cached value or access it somehow (via field or method)<br>
+ * <b>you should prefer getPropertyValue(AccessStrategy) instead of this method</b>
+ *
+ * @return the current value of the property accessed by reflection
+ * @throws IllegalArgumentException - error accessing attribute (config error, reflection problem)
+ * @throws IllegalStateException - when no property is currently set in the context (application logic bug)
+ */
+ public Object getPropertyValue() {
+ if (access == null) { // undefined access strategy
+ return getPropertyValue(
+ new PropertyAccess(bean.getClass(), metaProperty.getName()));
+ } else {
+ return getPropertyValue(access);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * Caches retrieved value.
+ */
+ public Object getPropertyValue(AccessStrategy access)
+ throws IllegalArgumentException, IllegalStateException {
+ if (propertyValue == UNKNOWN || (this.access != access && !fixed)) {
+ propertyValue = access.get(bean);
+ this.access = access;
+ }
+ return propertyValue;
+ }
+
+ /**
+ * Convenience method to access metaProperty.name
+ *
+ * @return null or the name of the current property
+ */
+ public String getPropertyName() {
+ return metaProperty == null ? null : metaProperty.getName();
+ }
+
+ /**
+ * Set the current property value.
+ * @param propertyValue
+ */
+ public void setPropertyValue(Object propertyValue) {
+ this.propertyValue = propertyValue;
+ }
+
+ /**
+ * Set the property value, fixed.
+ * @param value
+ */
+ public void setFixedValue(Object value) {
+ setPropertyValue(value);
+ setFixed(true);
+ }
+
+ /**
+ * Learn whether the current property value is "fixed."
+ * @return boolean
+ */
+ public boolean isFixed() {
+ return fixed;
+ }
+
+ /**
+ * Potentially declare the current property value as being "fixed."
+ * If <code>true</code>, the context will reuse any not-<code>UNKNOWN</code>
+ * propertyValue regardless of the {@link AccessStrategy} by which it is requested.
+ * @param fixed
+ */
+ public void setFixed(boolean fixed) {
+ this.fixed = fixed;
+ }
+
+ /**
+ * Depending on whether we have a metaProperty or not,
+ * this returns the metaProperty or otherwise the metaBean.
+ * This is used to have a simple way to request features
+ * in the Validation for both bean- and property-level validations.
+ *
+ * @return something that is capable to deliver features
+ */
+ public FeaturesCapable getMeta() {
+ return (metaProperty == null) ? metaBean : metaProperty;
+ }
+
+ /**
+ * Drop cached value, marking the internal cachedValue as <code>UNKNOWN</code>.
+ * This forces the BeanValidationContext to recompute the value
+ * the next time it is accessed.
+ * Use this method inside tests or when the propertyValue has been
+ * changed during validation.
+ */
+ public void unknownValue() {
+ propertyValue = UNKNOWN;
+ access = null;
+ fixed = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaBean getMetaBean() {
+ return metaBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getBean() {
+ return bean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaProperty getMetaProperty() {
+ return metaProperty;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMetaBean(MetaBean metaBean) {
+ this.metaBean = metaBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setBean(Object bean) {
+ this.bean = bean;
+ unknownValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setMetaProperty(MetaProperty metaProperty) {
+ this.metaProperty = metaProperty;
+ unknownValue();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "BeanValidationContext{ bean=" + bean + ", metaProperty=" + metaProperty +
+ ", propertyValue=" + propertyValue + '}';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void moveDown(MetaProperty prop, AccessStrategy access) {
+ setBean(getPropertyValue(access), prop.getMetaBean());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void moveUp(Object bean, MetaBean aMetaBean) {
+ setBean(bean, aMetaBean); // reset context state
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCurrentIndex(Integer index) {
+ // do nothing
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCurrentKey(Object key) {
+ // do nothing
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public AccessStrategy getAccess() {
+ return this.access;
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java
new file mode 100644
index 0000000..175e657
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.PropertyAccess;
+import org.apache.bval.util.ValidationHelper;
+
+// TODO: centralize treatMapsLikeBeans
+
+/**
+ * Description: Top-Level API-class to validate objects or object-trees. You can
+ * invoke, extend or utilize this class if you need other ways to integrate
+ * validation in your application.
+ * <p/>
+ * This class supports cyclic object graphs by keeping track of validated
+ * instances in the validation context.<br/>
+ */
+public class BeanValidator<T extends ValidationListener> {
+ private final MetaBeanFinder metaBeanFinder;
+
+ /**
+ * Create a new BeanValidator instance. Convenience constructor. Use the
+ * global instance of MetaBeanManagerFactory.getFinder().
+ */
+ public BeanValidator() {
+ this(MetaBeanManagerFactory.getFinder());
+ }
+
+ /**
+ * Create a new BeanValidator instance.
+ *
+ * @param metaBeanFinder
+ */
+ public BeanValidator(MetaBeanFinder metaBeanFinder) {
+ this.metaBeanFinder = metaBeanFinder;
+ }
+
+ /**
+ * Convenience API. validate a root object with all related objects with its
+ * default metaBean definition.
+ *
+ * @param bean
+ * @return results - validation results found
+ */
+ public T validate(Object bean) {
+ MetaBean metaBean = getMetaBeanFinder().findForClass(bean.getClass());
+ return validate(bean, metaBean);
+ }
+
+ /**
+ * Convenience API. validate a root object with all related objects
+ * according to the metaBean.
+ *
+ * @param bean
+ * - a single bean or a collection of beans (that share the same
+ * metaBean!)
+ * @param metaBean
+ * @return results - validation results found
+ */
+ public T validate(Object bean, MetaBean metaBean) {
+ ValidationContext<T> context = createContext();
+ context.setBean(bean, metaBean);
+ ValidationHelper.validateContext(context, new BeanValidatorCallback(context), treatMapsLikeBeans);
+ return context.getListener();
+ }
+
+ /**
+ * Validate the method parameters based on @Validate annotations.
+ * Requirements: Parameter, that are to be validated must be annotated with @Validate
+ *
+ * @param method
+ * - a method
+ * @param parameters
+ * - the parameters suitable to the method
+ * @return a validation result or null when there was nothing to validate
+ * @see Validate
+ */
+ public T validateCall(Method method, Object[] parameters) {
+ if (parameters.length > 0) {
+ // shortcut (for performance!)
+ Annotation[][] annotations = method.getParameterAnnotations();
+ ValidationContext<T> context = null;
+ for (int i = 0; i < parameters.length; i++) {
+ for (Annotation anno : annotations[i]) {
+ if (anno instanceof Validate) {
+ if (context == null)
+ context = createContext();
+ if (determineMetaBean((Validate) anno, parameters[i], context)) {
+ ValidationHelper.validateContext(context, new BeanValidatorCallback(context),
+ treatMapsLikeBeans);
+ break; // next parameter
+ }
+ }
+ }
+ }
+ return context != null ? context.getListener() : null;
+ }
+ return null;
+ }
+
+ /**
+ * Determine the metabean for the given object.
+ *
+ * @param <VL>
+ * @param validate
+ * @param parameter
+ * @param context
+ * @return true when validation should happen, false to skip it
+ */
+ protected <VL extends ValidationListener> boolean determineMetaBean(Validate validate, Object parameter,
+ ValidationContext<VL> context) {
+ if (validate.value().length() == 0) {
+ if (parameter == null)
+ return false;
+ Class<?> beanClass;
+ if (parameter instanceof Collection<?>) { // do not validate empty
+ // collection
+ Collection<?> coll = ((Collection<?>) parameter);
+ if (coll.isEmpty())
+ return false;
+ beanClass = coll.iterator().next().getClass(); // get first
+ // object
+ } else if (parameter.getClass().isArray()) {
+ beanClass = parameter.getClass().getComponentType();
+ } else {
+ beanClass = parameter.getClass();
+ }
+ context.setBean(parameter, getMetaBeanFinder().findForClass(beanClass));
+ } else {
+ context.setBean(parameter, getMetaBeanFinder().findForId(validate.value()));
+ }
+ return true;
+ }
+
+ /**
+ * factory method - overwrite in subclasses
+ *
+ * @return ValidationListener of the proper type
+ */
+ @SuppressWarnings("unchecked")
+ protected T createResults() {
+ return (T) new ValidationResults();
+ }
+
+ /**
+ * factory method - overwrite in subclasses
+ *
+ * @return ValidationContext parameterized with our listener type
+ */
+ protected ValidationContext<T> createContext() {
+ return new BeanValidationContext<T>(createResults());
+ }
+
+ /**
+ * Convenience API. validate a single property.
+ *
+ * @param bean
+ * - the root object
+ * @param metaProperty
+ * - metadata for the property
+ * @return validation results
+ */
+ public T validateProperty(Object bean, MetaProperty metaProperty) {
+ ValidationContext<T> context = createContext();
+ context.setBean(bean);
+ context.setMetaProperty(metaProperty);
+ ValidationHelper.validateProperty(context);
+ return context.getListener();
+ }
+
+ /**
+ * {@inheritDoc} internal validate a bean (=not a collection of beans) and
+ * its related beans
+ */
+ protected <VL extends ValidationListener> void validateBeanNet(ValidationContext<VL> context) {
+ if (context.collectValidated()) {
+ ValidationHelper.validateBean(context);
+ for (MetaProperty prop : context.getMetaBean().getProperties()) {
+ validateRelatedBean(context, prop);
+ }
+ }
+ }
+
+ /**
+ * Validate a property of a graph.
+ *
+ * @param <VL>
+ * @param context
+ * @param prop
+ */
+ protected <VL extends ValidationListener> void validateRelatedBean(ValidationContext<VL> context, MetaProperty prop) {
+ AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
+ if (access == null && prop.getMetaBean() != null) { // single property
+ // access strategy
+ // save old values from context
+ final Object bean = context.getBean();
+ final MetaBean mbean = context.getMetaBean();
+ // modify context state for relationship-target bean
+ context.moveDown(prop, new PropertyAccess(bean.getClass(), prop.getName()));
+ ValidationHelper.validateContext(context, new BeanValidatorCallback(context), treatMapsLikeBeans);
+ // restore old values in context
+ context.moveUp(bean, mbean);
+ } else if (access != null) { // different accesses to relation
+ // save old values from context
+ final Object bean = context.getBean();
+ final MetaBean mbean = context.getMetaBean();
+ for (AccessStrategy each : access) {
+ // modify context state for relationship-target bean
+ context.moveDown(prop, each);
+ ValidationHelper.validateContext(context, new BeanValidatorCallback(context), treatMapsLikeBeans);
+ // restore old values in context
+ context.moveUp(bean, mbean);
+ }
+ }
+ }
+
+ private boolean treatMapsLikeBeans = false;
+
+ public boolean isTreatMapsLikeBeans() {
+ return treatMapsLikeBeans;
+ }
+
+ public void setTreatMapsLikeBeans(boolean treatMapsLikeBeans) {
+ this.treatMapsLikeBeans = treatMapsLikeBeans;
+ }
+
+ /**
+ * Get the metabean finder associated with this validator.
+ *
+ * @return a MetaBeanFinder
+ * @see org.apache.bval.MetaBeanManagerFactory#getFinder()
+ */
+ public MetaBeanFinder getMetaBeanFinder() {
+ return metaBeanFinder;
+ }
+
+ /**
+ * Dispatches a call from {@link #validate()} to
+ * {@link BeanValidator#validateBeanNet(ValidationContext)} with the current
+ * context set.
+ */
+ private class BeanValidatorCallback implements ValidationHelper.ValidateCallback {
+
+ private final ValidationContext<?> context;
+
+ public BeanValidatorCallback(ValidationContext<?> context) {
+ this.context = context;
+ }
+
+ public void validate() {
+ validateBeanNet(context);
+ }
+
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/DynamicMetaBean.java b/trunk/bval-core/src/main/java/org/apache/bval/DynamicMetaBean.java
new file mode 100644
index 0000000..c5e22df
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/DynamicMetaBean.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import org.apache.bval.model.MetaBean;
+
+// TODO: Reduce visibility
+
+/**
+ * Description: Dynamic {@link MetaBean} subclass.<br/>
+ */
+public final class DynamicMetaBean extends MetaBean {
+ private static final long serialVersionUID = 1L;
+
+ private final MetaBeanFinder finder;
+
+ /**
+ * Create a new DynamicMetaBean instance.
+ * @param finder
+ */
+ public DynamicMetaBean(MetaBeanFinder finder) {
+ this.finder = finder;
+ }
+
+ /**
+ * {@inheritDoc}
+ * different strategies with hints to find MetaBean of associated object can
+ * be implemented here.
+ */
+ @Override
+ public MetaBean resolveMetaBean(Object bean) {
+ return bean instanceof Class<?> ?
+ finder.findForClass((Class<?>) bean) : finder.findForClass(bean.getClass());
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/IntrospectorMetaBeanFactory.java b/trunk/bval-core/src/main/java/org/apache/bval/IntrospectorMetaBeanFactory.java
new file mode 100644
index 0000000..d0c9e48
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/IntrospectorMetaBeanFactory.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+
+import java.beans.BeanInfo;
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Enumeration;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: use information from java.beans.Introspector in MetaBeans. The PropertyDescriptor can contain info about
+ * HIDDEN, PREFERRED, READONLY and other features<br/>
+ * NOTE: THIS IS AN OPTIONAL CLASS, TO ENABLE IT, SET Factory Property apache.bval.enable-introspector="true"
+ */
+public class IntrospectorMetaBeanFactory implements MetaBeanFactory {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void buildMetaBean(MetaBean meta) throws Exception {
+ if (meta.getBeanClass() == null) {
+ return; // handle only, when local class exists
+ }
+ BeanInfo info = Introspector.getBeanInfo(meta.getBeanClass());
+ if (meta.getName() == null && info.getBeanDescriptor() != null) {
+ meta.setName(info.getBeanDescriptor().getName()); // (display?)name = simple class name!
+ }
+ for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
+ if (!(pd instanceof IndexedPropertyDescriptor || pd.getName().equals("class"))) {
+ MetaProperty metaProp = buildMetaProperty(pd, meta.getProperty(pd.getName()));
+ meta.putProperty(pd.getName(), metaProp);
+ }
+ }
+ }
+
+ /**
+ * Create a {@link MetaProperty} from the specified {@link PropertyDescriptor}.
+ *
+ * @param pd
+ * @return MetaProperty
+ */
+ @Deprecated
+ protected MetaProperty buildMetaProperty(PropertyDescriptor pd) {
+ return buildMetaProperty(pd, null);
+ }
+
+ /**
+ * Create a {@link MetaProperty} from the specified {@link PropertyDescriptor}.
+ *
+ * @param pd
+ * @param existing
+ * @return MetaProperty
+ */
+ protected MetaProperty buildMetaProperty(PropertyDescriptor pd, MetaProperty existing) {
+ MetaProperty meta = new MetaProperty();
+ meta.setName(pd.getName());
+ meta.setType(determineGenericPropertyType(pd));
+ if (pd.isHidden()) {
+ meta.putFeature(HIDDEN, Boolean.TRUE);
+ }
+ if (pd.isPreferred()) {
+ meta.putFeature(PREFERRED, Boolean.TRUE);
+ }
+ if (pd.isConstrained()) {
+ meta.putFeature(READONLY, Boolean.TRUE);
+ }
+ Enumeration<String> enumeration = pd.attributeNames();
+ while (enumeration.hasMoreElements()) {
+ String key = enumeration.nextElement();
+ Object value = pd.getValue(key);
+ meta.putFeature(key, value);
+ }
+ return meta;
+ }
+
+ private Type determineGenericPropertyType(PropertyDescriptor pd) {
+ Method m = pd.getReadMethod();
+ if (m != null) {
+ return m.getGenericReturnType();
+ }
+ m = pd.getWriteMethod();
+ if (m != null && m.getParameterTypes().length == 1) {
+ return m.getGenericParameterTypes()[0];
+ }
+ return pd.getPropertyType();
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanBuilder.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanBuilder.java
new file mode 100644
index 0000000..745b0fa
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanBuilder.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description: internal implementation class to construct metabeans with
+ * factories<br/>
+ */
+public class MetaBeanBuilder {
+
+ private static final Logger log = Logger.getLogger(MetaBeanBuilder.class.getName());
+
+ /**
+ * here you can install different kinds of factories to create MetaBeans
+ * from
+ */
+ private MetaBeanFactory[] factories;
+
+ /**
+ * Create a new MetaBeanBuilder instance.
+ */
+ public MetaBeanBuilder() {
+ this(new MetaBeanFactory[] { new IntrospectorMetaBeanFactory() });
+ }
+
+ /**
+ * Create a new MetaBeanBuilder instance.
+ *
+ * @param factories
+ */
+ public MetaBeanBuilder(MetaBeanFactory[] factories) {
+ setFactories(factories);
+ }
+
+ /**
+ * Get the configured set of {@link MetaBeanFactory} objects.
+ *
+ * @return {@link MetaBeanFactory} array
+ */
+ public MetaBeanFactory[] getFactories() {
+ return ArrayUtils.clone(factories);
+ }
+
+ /**
+ * Set the array of {@link MetaBeanFactory} instances with which to enrich
+ * {@link MetaBean}s.
+ *
+ * @param factories
+ */
+ public void setFactories(MetaBeanFactory[] factories) {
+ this.factories = ArrayUtils.clone(factories);
+ }
+
+ /**
+ * Build a {@link MetaBean} for a given id.
+ *
+ * @param beanInfoId
+ * @return MetaBean
+ * @throws Exception
+ * if unable to build
+ */
+ public MetaBean buildForId(String beanInfoId) throws Exception {
+ throw new IllegalArgumentException("MetaBean " + beanInfoId + " not found");
+ }
+
+ /**
+ * Build beans for all known ids. Default implementation returns an empty
+ * map.
+ *
+ * @return Map of String : MetaBean
+ */
+ public Map<String, MetaBean> buildAll() throws Exception {
+ return new HashMap<String, MetaBean>();
+ }
+
+ /**
+ * Find the named class.
+ *
+ * @param className
+ * @return Class found or null
+ */
+ protected Class<?> findLocalClass(String className) {
+ if (className != null) {
+ try {
+ return ClassUtils.getClass(className);
+ } catch (ClassNotFoundException e) {
+ log.log(Level.FINE, String.format("Class not found: %s", className), e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Build a MetaBean for the specified class.
+ *
+ * @param clazz
+ * @return MetaBean
+ * @throws Exception
+ */
+ public MetaBean buildForClass(Class<?> clazz) throws Exception {
+ MetaBean meta = new MetaBean();
+ if (clazz != null) { // local class here?
+ meta.setBeanClass(clazz);
+ meta.setId(clazz.getName()); // default id = full class name!
+ }
+ for (MetaBeanFactory factory : factories) {
+ factory.buildMetaBean(meta);
+ }
+ return meta;
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanCache.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanCache.java
new file mode 100644
index 0000000..c55bc0a
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanCache.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.bval.model.MetaBean;
+
+/**
+ * Description: a cache to hold metabeans by id and by class.<br/>
+ */
+public class MetaBeanCache implements MetaBeanFinder, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Cache keyed by id.
+ */
+ protected final ConcurrentMap<String, MetaBean> cacheById = new ConcurrentHashMap<String, MetaBean>();
+ /**
+ * Cache keyed by class.
+ */
+ protected final ConcurrentMap<Class<?>, MetaBean> cacheByClass = new ConcurrentHashMap<Class<?>, MetaBean>();
+
+ /**
+ * Create a new MetaBeanCache instance.
+ */
+ public MetaBeanCache() {
+ super();
+ }
+
+ /**
+ * Create a new MetaBeanCache instance.
+ * @param beans
+ */
+ public MetaBeanCache(Map<String, MetaBean> beans) {
+ this();
+ for (MetaBean bean : beans.values()) {
+ cache(bean);
+ }
+ }
+
+ /**
+ * Clear the cache.
+ */
+ public void clear() {
+ cacheById.clear();
+ cacheByClass.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaBean findForId(String beanInfoId) {
+ return cacheById.get(beanInfoId);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaBean findForClass(Class<?> clazz) {
+ return cacheByClass.get(clazz);
+ }
+
+ /**
+ * Return all cached MetaBeans by id.
+ * @return live map
+ */
+ public Map<String, MetaBean> findAll() {
+ return cacheById;
+ }
+
+ /**
+ * Cache the specified MetaBean.
+ * @param beanInfo
+ */
+ public void cache(MetaBean beanInfo) {
+ cacheById.put(beanInfo.getId(), beanInfo);
+ if (beanInfo.getBeanClass() != null &&
+ beanInfo.getId().equals(beanInfo.getBeanClass().getName())) {
+ cacheByClass.putIfAbsent(beanInfo.getBeanClass(), beanInfo);
+ }
+ }
+
+ /**
+ * Remove a single MetaBean from the cache.
+ * @param beanInfo
+ */
+ public void removeFromCache(MetaBean beanInfo) {
+ cacheById.remove(beanInfo.getId());
+ if (beanInfo.getBeanClass() != null &&
+ beanInfo.getId().equals(beanInfo.getBeanClass().getName())) {
+ cacheByClass.remove(beanInfo.getBeanClass());
+ }
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFactory.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFactory.java
new file mode 100644
index 0000000..e5a06d7
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFactory.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import org.apache.bval.model.MetaBean;
+
+/**
+ * Description: interface for abstraction how to initialize a MetaBean
+ * with information from somewhere<br/>
+ */
+public interface MetaBeanFactory {
+ /**
+ * Initialize the specified {@link MetaBean}.
+ * @param metaBean
+ * @throws Exception
+ */
+ void buildMetaBean(MetaBean metaBean) throws Exception;
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFinder.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFinder.java
new file mode 100644
index 0000000..1caa97e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFinder.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+
+import org.apache.bval.model.MetaBean;
+
+/**
+ * Description: Interface to find BeanInfos <br/>
+ */
+public interface MetaBeanFinder {
+ /**
+ * Find a MetaBean with a certain id.
+ * @param beanInfoId - symbolic unique name of Meta Info
+ * @return BeanInfo
+ * @throws IllegalArgumentException - when MetaBean not found
+ */
+ MetaBean findForId(String beanInfoId);
+
+ /**
+ * Find the MetaBean for the specified class.
+ * @param clazz - bean class
+ * @return BeanInfo (never null)
+ */
+ MetaBean findForClass(Class<?> clazz);
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManager.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManager.java
new file mode 100644
index 0000000..fe4797e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManager.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: Default implementation for the interface to find, register and
+ * create MetaBeans. In most situations a single instance of this class is
+ * sufficient and you can get this instance from the
+ * {@link MetaBeanManagerFactory}. <br/>
+ */
+public class MetaBeanManager implements MetaBeanFinder {
+
+ /** MetaBean cache */
+ protected final MetaBeanCache cache = new MetaBeanCache();
+ /** MetaBean builder */
+ protected final MetaBeanBuilder builder;
+ /** Complete flag */
+ protected boolean complete = false;
+
+ /**
+ * Create a new MetaBeanManager instance.
+ */
+ public MetaBeanManager() {
+ builder = new MetaBeanBuilder();
+ }
+
+ /**
+ * Create a new MetaBeanManager instance.
+ *
+ * @param builder
+ */
+ public MetaBeanManager(MetaBeanBuilder builder) {
+ this.builder = builder;
+ }
+
+ /**
+ * Get the builder used.
+ *
+ * @return {@link MetaBeanBuilder}
+ */
+ public MetaBeanBuilder getBuilder() {
+ return builder;
+ }
+
+ /**
+ * Get the cache used.
+ *
+ * @return {@link MetaBeanCache}
+ */
+ public MetaBeanCache getCache() {
+ return cache;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaBean findForId(String beanInfoId) {
+ MetaBean beanInfo = cache.findForId(beanInfoId);
+ if (beanInfo != null)
+ return beanInfo;
+ try {
+ beanInfo = builder.buildForId(beanInfoId);
+ cache.cache(beanInfo);
+ computeRelationships(beanInfo);
+ return beanInfo;
+ } catch (RuntimeException e) {
+ throw e; // do not wrap runtime exceptions
+ } catch (Exception e) {
+ throw new IllegalArgumentException("error creating beanInfo with id: " + beanInfoId, e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaBean findForClass(Class<?> clazz) {
+ if (clazz == null)
+ return null;
+ MetaBean beanInfo = cache.findForClass(clazz);
+ if (beanInfo != null)
+ return beanInfo;
+ try {
+ beanInfo = builder.buildForClass(clazz);
+ cache.cache(beanInfo);
+ computeRelationships(beanInfo);
+ return beanInfo;
+ } catch (RuntimeException e) {
+ throw e; // do not wrap runtime exceptions
+ } catch (Exception e) {
+ throw new IllegalArgumentException("error creating beanInfo for " + clazz, e);
+ }
+ }
+
+ /**
+ * Compute all known relationships for <code>beanInfo</code>. must be called
+ * AFTER cache.cache() to avoid endless loop
+ *
+ * @param beanInfo
+ * - the bean for which to compute relationships
+ */
+ protected void computeRelationships(MetaBean beanInfo) {
+ for (MetaProperty prop : beanInfo.getProperties()) {
+ String beanRef = (String) prop.getFeature(REF_BEAN_ID);
+ computeRelatedMetaBean(prop, beanRef);
+ }
+ }
+
+ /**
+ * Compute a single related {@link MetaBean}.
+ *
+ * @param prop
+ * @param beanRef
+ */
+ protected void computeRelatedMetaBean(MetaProperty prop, String beanRef) {
+ Class<?> beanType = prop.getFeature(REF_BEAN_TYPE);
+ if (beanType != null) {
+ prop.setMetaBean(findForClass(beanType));
+ } else if (prop.getFeature(REF_CASCADE) != null) { // dynamic type
+ // resolution:
+ prop.setMetaBean(new DynamicMetaBean(this));
+ }
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManagerFactory.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManagerFactory.java
new file mode 100644
index 0000000..f2cd8da
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManagerFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+/**
+ * Description: API class to hold a singleton of a {@link MetaBeanManager}
+ * that implements the finder and registry interfaces for MetaBeans<br/>
+ *
+ * @see org.apache.bval.model.MetaBean
+ * @see MetaBeanManager
+ */
+public class MetaBeanManagerFactory {
+ private static MetaBeanManager manager = new MetaBeanManager();
+
+ /**
+ * global meta bean finder.
+ * @return the singleton
+ */
+ public static MetaBeanFinder getFinder() {
+ return manager;
+ }
+
+
+ /**
+ * set global meta bean manager, that is responsible
+ * for finding, caching, xml registry and enrichment algorithm.
+ * @param finder
+ */
+ public static void setManager(MetaBeanManager finder) {
+ manager = finder;
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/Validate.java b/trunk/bval-core/src/main/java/org/apache/bval/Validate.java
new file mode 100644
index 0000000..039106d
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/Validate.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: Annotate an element (parameter) to be validated.
+ * <br>
+ */
+@Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER})
+@Retention(RUNTIME)
+public @interface Validate {
+ /**
+ * (optional) the MetaBean.id to use
+ */
+ String value() default "";
+
+ /**
+ * to Support groups on @Valid(ate) in method/return validation
+ */
+ Class<?>[] groups() default {};
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/ValidationResults.java b/trunk/bval-core/src/main/java/org/apache/bval/ValidationResults.java
new file mode 100644
index 0000000..e9d79f0
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/ValidationResults.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Description: Implements a contains to hold and transport validation results<br/>
+ */
+public class ValidationResults implements ValidationListener, Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private Map<String, List<Error>> errorsByReason;
+ private Map<Object, Map<String, List<Error>>> errorsByOwner;
+
+
+ /**
+ * API to add an error to the validation results.
+ *
+ * @param reason - Features from {@link org.apache.bval.model.Features.Property}
+ * or custom reason of validation error
+ * @param context - context information (bean, propertyName, value, ...)
+ */
+ public <T extends ValidationListener> void addError(String reason, ValidationContext<T> context) {
+ Error error = createError(reason, context.getBean(), context.getPropertyName());
+ addError(error, context);
+ }
+
+
+ /**
+ * API to add an error to the validation results.
+ *
+ * @param error - holding the description of reason and object to describe
+ * the validation error
+ * @param context - null or the context to provide additional information
+ */
+ public <T extends ValidationListener> void addError(Error error, ValidationContext<T> context) {
+ if (errorsByReason == null) {
+ initialize();
+ }
+ addToReasonBucket(error);
+ addToOwnerBucket(error);
+ }
+
+ /**
+ * Old API to add an error to the validation results when no context is available.
+ *
+ * @param reason - Features from {@link org.apache.bval.model.Features.Property} or custom validation reason
+ * @param bean - (optional) owner bean or null
+ * @param propertyName - (optional) propertyName where valiation error occurred or null
+ */
+ public void addError(String reason, Object bean, String propertyName) {
+ addError(createError(reason, bean, propertyName), null);
+ }
+
+ /**
+ * Create an Error object.
+ * @param reason
+ * @param owner
+ * @param propertyName
+ * @return new {@link Error}
+ */
+ protected Error createError(String reason, Object owner, String propertyName) {
+ return new Error(reason, owner, propertyName);
+ }
+
+ /**
+ * initialize the error-buckets now when needed and
+ * not on instance creation to save memory garbage.
+ */
+ protected void initialize() {
+ errorsByReason = new LinkedHashMap<String, List<Error>>();
+ errorsByOwner = new LinkedHashMap<Object, Map<String, List<Error>>>();
+ }
+
+ /**
+ * Add an Error to the set of Errors shared by a particular "reason."
+ * @param error
+ * @see {@link Error#getReason()}
+ */
+ protected void addToReasonBucket(Error error) {
+ if (error.getReason() == null) return;
+
+ List<Error> errors = errorsByReason.get(error.getReason());
+ if (errors == null) {
+ errors = new ArrayList<Error>();
+ errorsByReason.put(error.getReason(), errors);
+ }
+ errors.add(error);
+ }
+
+ /**
+ * Add an Error to the property-keyed map of Errors maintained for this Error's owner.
+ * @param error
+ * @see {@link Error#getOwner()}
+ */
+ protected void addToOwnerBucket(Error error) {
+ if (error.getOwner() == null) return;
+
+ Map<String, List<Error>> errors = errorsByOwner.get(error.getOwner());
+ if (errors == null) {
+ errors = new HashMap<String, List<Error>>();
+ errorsByOwner.put(error.getOwner(), errors);
+ }
+ List<Error> list = errors.get(error.getPropertyName());
+ if (list == null) {
+ list = new ArrayList<Error>();
+ errors.put(error.getPropertyName(), list);
+ }
+ list.add(error);
+ }
+
+ /**
+ * Get the map of Errors by reason;
+ * key = reason, value = list of errors for this reason
+ * @return map
+ */
+ public Map<String, List<Error>> getErrorsByReason() {
+ if (errorsByReason == null) return Collections.emptyMap();
+ return errorsByReason;
+ }
+
+ /**
+ * Get the map of Errors by owner;
+ * key = owner, value = map with:<br>
+ * key = propertyName, value = list of errors for this owner.propertyName
+ * @return map
+ */
+ public Map<Object, Map<String, List<Error>>> getErrorsByOwner() {
+ if (errorsByOwner == null) return Collections.emptyMap();
+ return errorsByOwner;
+ }
+
+ /**
+ * Learn whether these results are empty/error-free.
+ * @return true when there are NO errors in this validation result
+ */
+ public boolean isEmpty() {
+ if (errorsByReason == null ||
+ (errorsByReason.isEmpty() && errorsByOwner.isEmpty())) return true;
+ for (List<Error> list : errorsByReason.values()) {
+ if (!list.isEmpty()) return false;
+ }
+ for (Map<String, List<Error>> map : errorsByOwner.values()) {
+ for (List<Error> list : map.values()) {
+ if (!list.isEmpty()) return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Learn whether there is an Error keyed to a specified reason description.
+ * @param reason
+ * @return boolean
+ * @see {@link Error#getReason()}
+ */
+ public boolean hasErrorForReason(String reason) {
+ if (errorsByReason == null) return false;
+ List<Error> errors = errorsByReason.get(reason);
+ return errors != null && !errors.isEmpty();
+ }
+
+ /**
+ * Learn whether <code>bean</code> has any errors keyed to property <code>propertyName</code>.
+ * @param bean
+ * @param propertyName - may be null: any property is checked
+ * OR the name of the property to check
+ * @return boolean
+ */
+ public boolean hasError(Object bean, String propertyName) {
+ if (errorsByOwner == null) return false;
+ Map<String, List<Error>> errors = errorsByOwner.get(bean);
+ if (errors == null) return false;
+ if (propertyName != null) {
+ List<Error> list = errors.get(propertyName);
+ return list != null && !list.isEmpty();
+ } else {
+ for (List<Error> list : errors.values()) {
+ if (!list.isEmpty()) return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "ValidationResults{" + errorsByOwner + "}";
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/DynaType.java b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaType.java
new file mode 100644
index 0000000..7a89862
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaType.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import java.lang.reflect.Type;
+
+/**
+ * Description: implementation of a dynamic type. can be used inside a
+ * MetaProperty for instance-based types <br/>
+ */
+public interface DynaType extends Type {
+ /**
+ * Resolve the type indirection.
+ * @return Type
+ */
+ Type getRawType();
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/DynaTypeEnum.java b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaTypeEnum.java
new file mode 100644
index 0000000..d520807
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaTypeEnum.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: ("artificial" enum with custom values).<br/>
+ */
+public class DynaTypeEnum implements DynaType {
+ private final Class<?> enumClass;
+ private Value[] enumConstants;
+
+ /**
+ * Create a new DynaTypeEnum instance.
+ * @param enumClass
+ */
+ public DynaTypeEnum(Class<?> enumClass) {
+ this.enumClass = enumClass;
+ }
+
+ /**
+ * Create a new DynaTypeEnum instance.
+ * @param enumClass
+ * @param names
+ */
+ public DynaTypeEnum(Class<?> enumClass, String... names) {
+ this(enumClass);
+ setEnumNames(names);
+ }
+
+ /**
+ * Set the enumeration value names.
+ * @param names
+ */
+ public void setEnumNames(String[] names) {
+ enumConstants = new Value[names.length];
+ int i = 0;
+ for (String each : names) {
+ enumConstants[i++] = new Value(each);
+ }
+ }
+
+ /**
+ * Get the name of the enum class.
+ * @return String
+ */
+ public String getName() {
+ return enumClass.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<?> getRawType() {
+ return enumClass;
+ }
+
+ /**
+ * Learn whether the referred class is, in fact, an enum class.
+ * used by freemarker-template "bean-infos-json.ftl"
+ */
+ public boolean isEnum() {
+ return enumClass.isEnum();
+ }
+
+ /**
+ * Get the emulated constants.
+ * used by freemarker-template "bean-infos-json.ftl"
+ * @return Value[]
+ */
+ public Value[] getEnumConstants() {
+ return ArrayUtils.clone(enumConstants);
+ }
+
+ /**
+ * Learn whether the wrapped class is assignable from <code>cls</code>.
+ * @param cls
+ * @return boolean
+ */
+ public boolean isAssignableFrom(Class<?> cls) {
+ return enumClass.isAssignableFrom(cls);
+ }
+
+ /**
+ * Represents a single "enum" instance (= the value).
+ */
+ public static final class Value {
+ final String name;
+
+ /**
+ * Create a new Value instance.
+ * @param name
+ */
+ Value(String name) {
+ this.name = name;
+ }
+
+ /**
+ * used by freemarker-template "bean-infos-json.ftl"
+ * @return the name of this constant
+ */
+ public String name() {
+ return name;
+ }
+
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/Features.java b/trunk/bval-core/src/main/java/org/apache/bval/model/Features.java
new file mode 100644
index 0000000..d362efa
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/Features.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+/**
+ * Description: Contains key of common feature keys used by standard validators etc.
+ * This DOES NOT MEAN that the list of property- or bean-features is closed. You can
+ * put anything into the metabean as a feature and use it in your custom validators
+ * and other classes that access your metabeans.<br/>
+ *
+ * @see FeaturesCapable
+ */
+public interface Features {
+ /** Features of {@link MetaBean} */
+ public interface Bean {
+ /** INFO: String, name of the Property, that is the Primary Key */
+ String MAIN_KEY = "mainKey";
+ /** INFO: category/domain to which the metaBean belongs to */
+ String DOMAIN = "domain";
+
+ // String DISPLAY_NAME = "displayName";
+ String UNIQUE_KEY = "uniqueKey";
+ }
+
+ /** Features of {@link MetaProperty} */
+ public interface Property {
+ /** INFO: possible Enum values */
+ String ENUM = "enum";
+ /** INFO: Boolean, TRUE if Property is a Unique Key */
+ String UNIQUE_KEY = "uniqueKey";
+ /** VALIDATION: Boolean, mandatory field? */
+ String MANDATORY = "mandatory";
+ /** VALIDATION: Integer, max. number of chars/digits / max. cardinality of a to-many relationship */
+ String MAX_LENGTH = "maxLen";
+ /** VALIDATION: Comparable (e.g. a subclass of Number), max value */
+ String MAX_VALUE = "maxValue";
+ /** VALIDATION: Integer, min. number of chars/digits / min. cardinality of a to-many relationship */
+ String MIN_LENGTH = "minLen";
+ /** VALIDATION: Comparable (e.g. a subclass of Number), min value */
+ String MIN_VALUE = "minValue";
+ /** INFO: String-representation of a default value */
+ String DEFAULT_VALUE = "defValue";
+ /** SECURITY, INFO: Boolean, is value or relationship unmodifiable */
+ String READONLY = "readonly";
+ /**
+ * SECURITY, INFO: Boolean, Field accessible?
+ * If false, the field must not be displayed, queried, changed.
+ */
+ String DENIED = "denied";
+ /** VALIDATION: String, regular expression to validate the format of input data */
+ String REG_EXP = "regExp";
+ /**
+ * VALIDATION: String, Constraint for time-information of a Date-field:
+ * {@link org.apache.bval.xml.XMLMetaValue#TIMELAG_Past}
+ * or
+ * {@link org.apache.bval.xml.XMLMetaValue#TIMELAG_Future}
+ */
+ String TIME_LAG = "timeLag";
+
+ /**
+ * INFO: Boolean, Field visible?
+ *
+ * @see java.beans.PropertyDescriptor#isHidden()
+ */
+ String HIDDEN = "hidden";
+ /**
+ * INFO: Boolean
+ *
+ * @see java.beans.PropertyDescriptor#isPreferred()
+ */
+ String PREFERRED = "preferred";
+
+ /** INFO: relationship's target metaBean.id * */
+ String REF_BEAN_ID = "refBeanId";
+
+ /**
+ * INFO: Class<br>
+ * Relationship's target metaBean.beanClass.
+ * In case of to-many relationships, this feature
+ * hold the Bean-type not the Collection-type.
+ */
+ String REF_BEAN_TYPE = "refBeanType";
+
+ /**
+ * INFO: AccessStrategy[]<br>
+ * an array of accessStrategies
+ * how validation should cascade into relationship target beans<br>
+ * null when validation should NOT cascade into relationship target
+ * beans<br>
+ * <p/>
+ * Default: {PropertyAccess(metaProperty.name)},
+ * when MetaProperty.metaBean is != null
+ */
+ String REF_CASCADE = "refCascade";
+
+ /** INFO: an array with the string names of custom java script validation functions */
+ @Deprecated // TODO RSt - I suggest to remove this and all related code
+ String JAVASCRIPT_VALIDATION_FUNCTIONS = "jsFunctions";
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/FeaturesCapable.java b/trunk/bval-core/src/main/java/org/apache/bval/model/FeaturesCapable.java
new file mode 100644
index 0000000..d495dc1
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/FeaturesCapable.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: abstract superclass of meta objects that support a map of
+ * features.<br/>
+ */
+public abstract class FeaturesCapable implements Serializable {
+ private static final long serialVersionUID = -4045110242904814218L;
+
+ private ConcurrentMap<String, Object> features = createFeaturesMap();
+
+ /** key = validation id, value = the validation */
+ private Validation[] validations = new Validation[0];
+
+ private volatile boolean locking;
+ private ReentrantLock lock = new ReentrantLock(true);
+
+ /**
+ * Create a new FeaturesCapable instance.
+ */
+ public FeaturesCapable() {
+ super();
+ }
+
+ /**
+ * Get the (live) map of features.
+ *
+ * @return Map<String, Object>
+ */
+ public Map<String, Object> getFeatures() {
+ return features;
+ }
+
+ /**
+ * Set whether to optimize read operations by accessing the features map in
+ * an unsynchronized manner.
+ *
+ * @param fast
+ */
+ public void optimizeRead(boolean fast) {
+ lock.lock();
+ try {
+ this.locking = !fast;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Get the specified feature.
+ *
+ * @param <T>
+ * @param key
+ * @return T
+ */
+ public <T> T getFeature(String key) {
+ return getFeature(key, (T) null);
+ }
+
+ /**
+ * Get the specified feature, returning <code>defaultValue</code> if
+ * undeclared.
+ *
+ * @param <T>
+ * @param key
+ * @param defaultValue
+ * @return T
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T getFeature(String key, T defaultValue) {
+ boolean release = acquireLockIfNeeded();
+ try {
+ return features.containsKey(key) ? (T) features.get(key) : defaultValue;
+ } finally {
+ if (release) {
+ lock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Convenience method to set a particular feature value.
+ *
+ * @param <T>
+ * @param key
+ * @param value
+ */
+ public <T> void putFeature(String key, T value) {
+ boolean release = acquireLockIfNeeded();
+ try {
+ features.put(key, value);
+ } finally {
+ if (release) {
+ lock.unlock();
+ }
+ }
+ }
+
+ /**
+ * Create a deep copy (copy receiver and copy properties).
+ *
+ * @param <T>
+ * @return new T instance
+ */
+ public <T extends FeaturesCapable> T copy() {
+ try {
+ @SuppressWarnings("unchecked")
+ final T self = (T) clone();
+ copyInto(self);
+ return self;
+ } catch (CloneNotSupportedException e) {
+ throw new IllegalStateException("cannot clone() " + this, e);
+ }
+ }
+
+ /**
+ * Copy this {@link FeaturesCapable} into another {@link FeaturesCapable}
+ * instance.
+ *
+ * @param <T>
+ * @param target
+ */
+ protected <T extends FeaturesCapable> void copyInto(T target) {
+ target.features = target.createFeaturesMap();
+ target.features.putAll(features);
+ if (validations != null) {
+ target.validations = validations.clone();
+ }
+ }
+
+ /**
+ * Get any validations set for this {@link FeaturesCapable}.
+ *
+ * @return Validation array
+ */
+ public Validation[] getValidations() {
+ return ArrayUtils.clone(validations);
+ }
+
+ /**
+ * Set the validations for this {@link FeaturesCapable}.
+ *
+ * @param validations
+ */
+ public void setValidations(Validation[] validations) {
+ this.validations = ArrayUtils.clone(validations);
+ }
+
+ /**
+ * Add a validation to this {@link FeaturesCapable}.
+ *
+ * @param validation
+ * to add
+ */
+ public void addValidation(Validation validation) {
+ validations = ArrayUtils.add(validations, validation);
+ }
+
+ /**
+ * Search for an equivalent validation among those configured.
+ *
+ * @param aValidation
+ * @return true if found
+ */
+ public boolean hasValidation(Validation aValidation) {
+ if (validations == null)
+ return false;
+ for (Validation validation : validations) {
+ if (validation.equals(aValidation))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Create a features map for this {@link FeaturesCapable} object.
+ * @return ConcurrentMap
+ */
+ protected ConcurrentMap<String, Object> createFeaturesMap() {
+ return new ConcurrentHashMap<String, Object>();
+ }
+
+ private boolean acquireLockIfNeeded() {
+ if (locking) {
+ lock.lock();
+ // double read
+ if (locking) {
+ return true;
+ }
+ lock.unlock();
+ }
+ return false;
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/MetaBean.java b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaBean.java
new file mode 100644
index 0000000..05ce598
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaBean.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: the meta description of a bean or class. the class/bean itself can have a map of features and an array
+ * of metaproperties.<br/>
+ *
+ * @see MetaProperty
+ */
+public class MetaBean extends FeaturesCapable implements Cloneable, Features.Bean {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Comparator for managing the sorted properties array.
+ */
+ private static class PropertyNameComparator implements Comparator<Object> {
+ /** Static instance */
+ static final PropertyNameComparator INSTANCE = new PropertyNameComparator();
+
+ /**
+ * {@inheritDoc}
+ */
+ public int compare(Object o1, Object o2) {
+ return getName(o1).compareTo(getName(o2));
+ }
+
+ private String getName(Object o) {
+ if (o == null) {
+ throw new NullPointerException();
+ }
+ return o instanceof MetaProperty ? ((MetaProperty) o).getName() : String.valueOf(o);
+ }
+ }
+
+ private String id;
+ private String name;
+ private Class<?> beanClass;
+ private MetaProperty[] properties = new MetaProperty[0];
+
+ /**
+ * Get the id.
+ *
+ * @return String
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Set the id.
+ *
+ * @param id
+ * the String to set
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * Get the name.
+ *
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name.
+ *
+ * @param name
+ * the String to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the beanClass.
+ *
+ * @return Class<?>
+ */
+ public Class<?> getBeanClass() {
+ return beanClass;
+ }
+
+ /**
+ * Set the beanClass.
+ *
+ * @param beanClass
+ * the Class<?> to set
+ */
+ public void setBeanClass(Class<?> beanClass) {
+ this.beanClass = beanClass;
+ }
+
+ /**
+ * Get the properties.
+ *
+ * @return MetaProperty[]
+ */
+ public MetaProperty[] getProperties() {
+ return ArrayUtils.clone(properties);
+ }
+
+ /**
+ * Set the properties.
+ *
+ * @param properties
+ * the MetaProperty[] to set
+ */
+ public void setProperties(MetaProperty[] properties) {
+ this.properties = ArrayUtils.clone(properties);
+ Arrays.sort(this.properties, PropertyNameComparator.INSTANCE);
+ }
+
+ /**
+ * Get the specified {@link MetaProperty}.
+ *
+ * @param name
+ * @return MetaProperty found or <code>null</code>
+ */
+ public MetaProperty getProperty(String name) {
+ final MetaProperty[] props = properties;
+ int pos = Arrays.binarySearch(props, name, PropertyNameComparator.INSTANCE);
+ return pos < 0 ? null : props[pos];
+ }
+
+ /**
+ * Learn whether any known property is a relationship.
+ *
+ * @see MetaProperty#isRelationship()
+ * @return true when at least one of the properties is a relationship
+ */
+ public boolean hasRelationships() {
+ for (MetaProperty p : properties) {
+ if (p.isRelationship()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether there are any known properties.
+ *
+ * @return boolean
+ */
+ public boolean hasProperties() {
+ return properties.length > 0;
+ }
+
+ /**
+ * bidirectional - set the relationship between a MetaProperty and its parentMetaBean
+ *
+ * @param name
+ * @param property
+ * if <code>null</code>, remove
+ */
+ public void putProperty(String name, MetaProperty property) {
+ if (property != null) {
+ property.setParentMetaBean(this);
+ }
+ Object key = property == null ? name : property;
+ // make a local copy for consistency
+ MetaProperty[] props = properties;
+ int pos = Arrays.binarySearch(props, key, PropertyNameComparator.INSTANCE);
+ if (pos < 0) {
+ if (property == null) {
+ // store null property for unknown name == NOOP
+ return;
+ }
+ props = ArrayUtils.add(props, 0 - pos - 1, property);
+ } else {
+ if (property == null) {
+ props = ArrayUtils.remove(props, pos);
+ } else {
+ props[pos] = property;
+ }
+ }
+ this.properties = props;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "MetaBean{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", beanClass=" + beanClass + '}';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected <T extends FeaturesCapable> void copyInto(T target) {
+ super.copyInto(target);
+ final MetaBean copy = (MetaBean) target;
+ if (properties != null) {
+ copy.properties = properties.clone();
+ for (int i = copy.properties.length - 1; i >= 0; i--) {
+ copy.properties[i] = copy.properties[i].copy();
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * If this {@link MetaBean} is compatible with <code>bean</code>, return <code>this</code>, else <code>null</code>.
+ * </p>
+ * <p>
+ * Compatibility is satisfied in one of the following ways:
+ * <ul>
+ * <li><code>bean</code> is null</li>
+ * <li><code>bean</code> is an instance of our <code>beanClass</code></li>
+ * <li><code>bean</code> <em>is</em> our <code>beanClass</code> itself</li>
+ * </ul>
+ * </p>
+ *
+ * @param bean
+ * @return <code>this</code> or <code>null</code>
+ */
+ public MetaBean resolveMetaBean(Object bean) {
+ return bean == null || bean == beanClass || beanClass.isInstance(bean) ? this : null;
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/MetaProperty.java b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaProperty.java
new file mode 100644
index 0000000..48ddf38
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaProperty.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import java.lang.reflect.Type;
+
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * Description: the meta description of a property of a bean. It supports a map
+ * of features and multiple validations.<br/>
+ *
+ * @see Validation
+ * @see MetaBean
+ */
+public class MetaProperty extends FeaturesCapable
+ implements Cloneable, Features.Property {
+ private static final long serialVersionUID = 1L;
+
+ private String name;
+
+ private Type type;
+ private MetaBean metaBean;
+ private MetaBean parentMetaBean;
+
+ /**
+ * Create a new MetaProperty instance.
+ */
+ public MetaProperty() {
+ }
+
+ /**
+ * Get the metabean of the target bean (mainly for relationships).
+ * @return MetaBean (may be null).
+ */
+ public MetaBean getMetaBean() {
+ return metaBean;
+ }
+
+ /**
+ * Set the MetaBean of this {@link MetaProperty}.
+ * @param metaBean to set
+ */
+ public void setMetaBean(MetaBean metaBean) {
+ this.metaBean = metaBean;
+ }
+
+ /**
+ * Get the metabean that owns this property (set by MetaBean.putProperty())
+ * @return
+ */
+ public MetaBean getParentMetaBean() {
+ return parentMetaBean;
+ }
+
+ /**
+ * Set the metabean that owns this property (usually called by MetaBean.putProperty())
+ * @param parentMetaBean
+ */
+ void setParentMetaBean(MetaBean parentMetaBean) {
+ this.parentMetaBean = parentMetaBean;
+ }
+
+ /**
+ * Learn whether this property is considered a relationship.
+ * @return <code>true</code> if it has a MetaBean of its own
+ */
+ public boolean isRelationship() {
+ return metaBean != null;
+ }
+
+ /**
+ * Set the type of this property.
+ * @param type to set
+ */
+ public void setType(Type type) {
+ this.type = type;
+ }
+
+ /**
+ * Get the type of this property.
+ * @return
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Resolve the type of this property to a class.
+ * @return Class, <code>null</code> if cannot be determined
+ */
+ public Class<?> getTypeClass() {
+ Type targetType = type instanceof DynaType ? ((DynaType) type)
+ .getRawType() : type;
+ if (targetType == null) {
+ return null;
+ }
+ Type assigningType = getParentMetaBean() == null ? null
+ : getParentMetaBean().getBeanClass();
+ return TypeUtils.getRawType(targetType, assigningType);
+ }
+
+ /**
+ * Get the name of this property.
+ * @return String
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Learn whether this property is considered mandatory.
+ * @return <code>true</code> if the <code>MANDATORY</code> feature is set to <code>true</code>.
+ * @see {@link Features.Property#MANDATORY}
+ */
+ public boolean isMandatory() {
+ return getFeature(MANDATORY, Boolean.FALSE).booleanValue();
+ }
+
+ /**
+ * Set this property as being mandatory (or not).
+ * @param mandatory
+ * @see {@link Features.Property#MANDATORY}
+ */
+ public void setMandatory(boolean mandatory) {
+ putFeature(MANDATORY, Boolean.valueOf(mandatory));
+ }
+
+ /**
+ * Get javascript validations of this property.
+ * @return String[]
+ * @deprecated
+ */
+ @Deprecated // remove this method?
+ public String[] getJavaScriptValidations() {
+ return getFeature(JAVASCRIPT_VALIDATION_FUNCTIONS);
+ }
+
+ /**
+ * Set the name of this property.
+ * @param name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaProperty clone() throws CloneNotSupportedException {
+ return (MetaProperty) super.clone();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "MetaProperty{" + "name='" + name + '\'' + ", type=" + type + '}';
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/Validation.java b/trunk/bval-core/src/main/java/org/apache/bval/model/Validation.java
new file mode 100644
index 0000000..a81e731
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/Validation.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+/**
+ * Description: Interface for a single validation <br/>
+ */
+public interface Validation {
+ /**
+ * Perform a single validation routine.
+ * Validate the object or property according to the current ValidationContext.
+ *
+ * @param context - to access the property, value, constraints
+ */
+ <T extends ValidationListener> void validate(ValidationContext<T> context);
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationContext.java b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationContext.java
new file mode 100644
index 0000000..900444a
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationContext.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import org.apache.bval.util.AccessStrategy;
+
+/**
+ * Description: Interface of the context that holds all state information
+ * during the validation process<br/>
+ */
+public interface ValidationContext<T extends ValidationListener> {
+ /**
+ * Get the property value.
+ * @return {@link Object}
+ */
+ Object getPropertyValue();
+
+ /**
+ * Get the value by using the given access strategy.
+ * @param access
+ * @return {@link Object}
+ */
+ Object getPropertyValue(AccessStrategy access);
+
+ /**
+ * Get the property name.
+ * @return {@link String}
+ */
+ String getPropertyName();
+
+ /**
+ * Get the {@link ValidationListener}.
+ * @return T
+ */
+ T getListener();
+
+ /**
+ * Get the bean.
+ * @return {@link Object}
+ */
+ Object getBean();
+
+ /**
+ * Get the model meta-bean.
+ * @return {@link MetaBean}
+ */
+ MetaBean getMetaBean();
+
+ /**
+ * Set the model meta-bean.
+ * @param metaBean
+ */
+ void setMetaBean(MetaBean metaBean);
+
+ /**
+ * Get the model meta-property.
+ * @return {@link MetaProperty}
+ */
+ MetaProperty getMetaProperty();
+
+ /**
+ * Set the bean.
+ * @param bean
+ */
+ void setBean(Object bean);
+
+ /**
+ * Avoid recursion by recording the current state of this context as having been validated.
+ * <p/>
+ *
+ * @return true when this state had not already been recorded
+ */
+ boolean collectValidated();
+
+ /**
+ * Set the current bean/metabean.
+ * @param aBean
+ * @param aMetaBean
+ */
+ void setBean(Object aBean, MetaBean aMetaBean);
+
+ /**
+ * Set the current meta-property.
+ * @param metaProperty
+ */
+ void setMetaProperty(MetaProperty metaProperty);
+
+ /**
+ * Step deeper into association at 'prop'
+ * @param prop
+ * @param access
+ */
+ void moveDown(MetaProperty prop, AccessStrategy access);
+
+ /**
+ * Step out from a validation of associated objects.
+ * @param bean
+ * @param metaBean
+ */
+ void moveUp(Object bean, MetaBean metaBean);
+
+ /**
+ * Set the index of the object currently validated into the context.
+ * used to create the propertyPath with [index] information for collections.
+ * @param index
+ */
+ void setCurrentIndex(Integer index);
+
+ /**
+ * set the key of the object in a map currently validated into the context.
+ * used to create the propertyPath with [key] information for maps.
+ * @param key
+ */
+ void setCurrentKey(Object key);
+
+ /**
+ * Get the current access strategy.
+ * @return {@link AccessStrategy}
+ */
+ AccessStrategy getAccess();
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationListener.java b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationListener.java
new file mode 100644
index 0000000..1a41deb
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationListener.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Description: The interface to collect errors found during validation<br/>
+ */
+public interface ValidationListener {
+ /**
+ * Simple API to add an error reason during validation.
+ * Error notification added from a {@link Validation} with context information
+ * taken from the given {@link ValidationContext}.
+ *
+ * @param reason a constant describing the reason. This is normally the key of the
+ * feature that was violated in the object 'owner' for property 'propertyName'
+ * @param context - contains
+ * bean = the object that contains the error (owner)
+ * propertyName = the Name of the attribute that caused the error
+ */
+ <T extends ValidationListener> void addError(String reason, ValidationContext<T> context);
+
+ /** Alternative method to add a fully initialized {@link ValidationListener.Error} object. */
+ <T extends ValidationListener> void addError(Error error, ValidationContext<T> context);
+
+ /**
+ * An object holding a single validation constraint violation
+ * found during the validation process.
+ */
+ public class Error implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /** Reason */
+ final String reason;
+ /** Owner */
+ final Object owner;
+ /** Property name*/
+ final String propertyName;
+
+ /**
+ * Create a new Error instance.
+ * @param aReason
+ * @param aOwner
+ * @param aPropertyName
+ */
+ public Error(String aReason, Object aOwner, String aPropertyName) {
+ this.reason = aReason;
+ this.owner = aOwner;
+ this.propertyName = aPropertyName;
+ }
+
+ /**
+ * Get the reason.
+ * @return String
+ */
+ public String getReason() {
+ return reason;
+ }
+
+ /**
+ * Get the owner.
+ * @return Object
+ */
+ public Object getOwner() {
+ return owner;
+ }
+
+ /**
+ * Get the propertyName.
+ * @return String
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "Error{" + "reason='" + reason + '\'' + ", propertyName='" +
+ propertyName + '\'' + '}';
+ }
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java b/trunk/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
new file mode 100644
index 0000000..086da02
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.routines;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Description: holds the regexp to validate an email address<br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:40:59<br>
+ */
+public class EMailValidationUtils {
+ private static String ATOM = "[^\\x00-\\x1F^\\(^\\)^\\<^\\>^\\@^\\,^\\;^\\:^\\\\^\\\"^\\.^\\[^\\]^\\s]";
+ private static String DOMAIN = "(" + ATOM + "+(\\." + ATOM + "+)*";
+ private static String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";
+ public static final java.util.regex.Pattern DEFAULT_EMAIL_PATTERN;
+
+ static {
+ DEFAULT_EMAIL_PATTERN =
+ java.util.regex.Pattern.compile("^" + ATOM + "+(\\." + ATOM + "+)*@" + DOMAIN + "|" + IP_DOMAIN + ")$",
+ java.util.regex.Pattern.CASE_INSENSITIVE);
+ }
+
+ /**
+ * Learn whether a given object is a valid email address.
+ *
+ * @param value
+ * to check
+ * @return <code>true</code> if the validation passes
+ */
+ public static boolean isValid(Object value) {
+ return isValid(value, DEFAULT_EMAIL_PATTERN);
+ }
+
+ /**
+ * Learn whether a particular value matches a given pattern per
+ * {@link Matcher#matches()}.
+ *
+ * @param value
+ * @param aPattern
+ * @return <code>true</code> if <code>value</code> was a <code>String</code>
+ * matching <code>aPattern</code>
+ */
+ // TODO it would seem to make sense to move or reduce the visibility of this
+ // method as it is more general than email.
+ public static boolean isValid(Object value, Pattern aPattern) {
+ if (value == null)
+ return true;
+ if (!(value instanceof CharSequence))
+ return false;
+ CharSequence seq = (CharSequence) value;
+ if (seq.length() == 0)
+ return true;
+ Matcher m = aPattern.matcher(seq);
+ return m.matches();
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/AccessStrategy.java b/trunk/bval-core/src/main/java/org/apache/bval/util/AccessStrategy.java
new file mode 100644
index 0000000..be97ebc
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/AccessStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+
+/**
+ * Description: abstract class to encapsulate different strategies
+ * to get the value of a Property. This class is designed such that
+ * subclasses are intended to know internally to which property they refer,
+ * with only the particular target instance being externally required
+ * to calculate the property's value. One intent of this design is
+ * that the notion of the very definition of a property is abstracted
+ * along with the mechanism for accessing that property.<br/>
+ */
+public abstract class AccessStrategy {
+ /**
+ * Get the value from the given instance.
+ * @param instance
+ * @return the value
+ * @throws IllegalArgumentException in case of an error
+ */
+ public abstract Object get(Object instance);
+
+ /**
+ * Get the Java program {@link ElementType} used by this {@link AccessStrategy}
+ * to determine property values.
+ * @return ElementType
+ */
+ public abstract ElementType getElementType();
+
+ /**
+ * Get the type of the property
+ * @return Type
+ */
+ public abstract Type getJavaType();
+
+ /**
+ * Get a name representative of this property.
+ * @return String
+ */
+ public abstract String getPropertyName();
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/BValVersion.java b/trunk/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
new file mode 100644
index 0000000..49c0a38
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * This class contains version information for BVal.
+ * It uses Ant's filter tokens to convert the template into a java
+ * file with current information.
+ */
+public class BValVersion {
+
+ /** Project name */
+ public static final String PROJECT_NAME = "Apache BVal";
+ /** Unique id of the current project/version/revision */
+ public static final String PROJECT_ID;
+ /** Version number */
+ public static final String VERSION_NUMBER;
+ /** Major release number */
+ public static final int MAJOR_RELEASE;
+ /** Minor release number */
+ public static final int MINOR_RELEASE;
+ /** Patch/point release number */
+ public static final int PATCH_RELEASE;
+ /** Release status */
+ public static final String RELEASE_STATUS;
+ /** Version control revision number */
+ public static final String REVISION_NUMBER;
+
+ static {
+ Properties revisionProps = new Properties();
+ try {
+ InputStream in = BValVersion.class.getResourceAsStream
+ ("/META-INF/org.apache.bval.revision.properties");
+ if (in != null) {
+ try {
+ revisionProps.load(in);
+ } finally {
+ in.close();
+ }
+ }
+ } catch (IOException ioe) {
+ }
+
+ String vers = revisionProps.getProperty("project.version");
+ if (vers == null || "".equals(vers.trim()))
+ vers = "0.0.0";
+ VERSION_NUMBER = vers;
+
+ StringTokenizer tok = new StringTokenizer(VERSION_NUMBER, ".-");
+ int major, minor, patch;
+ try {
+ major = tok.hasMoreTokens() ? Integer.parseInt(tok.nextToken()) : 0;
+ } catch (Exception e) {
+ major = 0;
+ }
+
+ try {
+ minor = tok.hasMoreTokens() ? Integer.parseInt(tok.nextToken()) : 0;
+ } catch (Exception e) {
+ minor = 0;
+ }
+
+ try {
+ patch = tok.hasMoreTokens() ? Integer.parseInt(tok.nextToken()) : 0;
+ } catch (Exception e) {
+ patch = 0;
+ }
+
+ String revision = revisionProps.getProperty("svn.revision");
+ if (revision == null || "".equals(revision.trim())) {
+ revision = "unknown";
+ } else {
+ tok = new StringTokenizer(revision, ":");
+ String strTok = null;
+ while (tok.hasMoreTokens()) {
+ try {
+ strTok = tok.nextToken();
+ } catch (Exception e) {
+ }
+ }
+ if (strTok != null)
+ revision = strTok;
+ }
+
+ MAJOR_RELEASE = major;
+ MINOR_RELEASE = minor;
+ PATCH_RELEASE = patch;
+ RELEASE_STATUS = tok.hasMoreTokens() ? tok.nextToken("!") : "";
+ REVISION_NUMBER = revision;
+ PROJECT_ID = PROJECT_NAME + " " + VERSION_NUMBER + "-r" + REVISION_NUMBER;
+ }
+
+ /**
+ * Get the project version number.
+ * @return String
+ */
+ public static String getVersion() {
+ return VERSION_NUMBER;
+ }
+
+ /**
+ * Get the version control revision number.
+ * @return String
+ */
+ public static String getRevision() {
+ return REVISION_NUMBER;
+ }
+
+ /**
+ * Get the project name.
+ * @return String
+ */
+ public static String getName() {
+ return PROJECT_NAME;
+ }
+
+ /**
+ * Get the fully-qualified project id.
+ * @return String
+ */
+ public static String getID() {
+ return PROJECT_ID;
+ }
+
+ /**
+ * Main method of this class that prints the {@link #toString()} to <code>System.out</code>.
+ * @param args ignored
+ */
+ public static void main(String [] args) {
+ System.out.println(new BValVersion().toString());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder(80 * 40);
+ appendBanner(buf);
+ buf.append("\n");
+
+ appendProperty("os.name", buf).append("\n");
+ appendProperty("os.version", buf).append("\n");
+ appendProperty("os.arch", buf).append("\n\n");
+
+ appendProperty("java.version", buf).append("\n");
+ appendProperty("java.vendor", buf).append("\n\n");
+
+ buf.append("java.class.path:\n");
+ StringTokenizer tok = new StringTokenizer(
+ PrivilegedActions.getProperty("java.class.path"));
+ while (tok.hasMoreTokens()) {
+ buf.append("\t").append(tok.nextToken());
+ buf.append("\n");
+ }
+ buf.append("\n");
+
+ appendProperty("user.dir", buf).append("\n");
+ return buf.toString();
+ }
+
+ private void appendBanner(StringBuilder buf) {
+ buf.append("Project").append(": ").append(getName());
+ buf.append("\n");
+ buf.append("Version").append(": ").append(getVersion());
+ buf.append("\n");
+ buf.append("Revision").append(": ").append(getRevision());
+ buf.append("\n");
+ }
+
+ private StringBuilder appendProperty(String prop, StringBuilder buf) {
+ return buf.append(prop).append(": ").append(
+ PrivilegedActions.getProperty(prop));
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/FieldAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/FieldAccess.java
new file mode 100644
index 0000000..9cbc31e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/FieldAccess.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Description: direct field access strategy.<br/>
+ */
+public class FieldAccess extends AccessStrategy {
+
+ private final Field field;
+
+ /**
+ * Create a new FieldAccess instance.
+ * @param field
+ */
+ public FieldAccess(final Field field) {
+ this.field = field;
+ if (!field.isAccessible()) {
+ run(new PrivilegedAction<Void>() {
+ public Void run() {
+ field.setAccessible(true);
+ return null;
+ }
+ });
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object get(final Object instance) {
+ try {
+ return field.get(instance);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ElementType getElementType() {
+ return ElementType.FIELD;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Type getJavaType() {
+ return field.getGenericType();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPropertyName() {
+ return field.getName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return field.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FieldAccess that = (FieldAccess) o;
+
+ return field.equals(that.field);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int hashCode() {
+ return field.hashCode();
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/IndexedAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/IndexedAccess.java
new file mode 100644
index 0000000..315c25e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/IndexedAccess.java
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * {@link AccessStrategy} to get an indexed member of an {@link Iterable} or
+ * array object.
+ */
+public class IndexedAccess extends AccessStrategy {
+ private static final TypeVariable<?> ITERABLE_TYPE = Iterable.class.getTypeParameters()[0];
+
+ /**
+ * Get the Java element type of a particular container type.
+ *
+ * @param containerType
+ * @return Type or <code>null</code> if <code>containerType</code> is not
+ * some type of {@link Iterable} or array
+ */
+ public static Type getJavaElementType(Type containerType) {
+ if (TypeUtils.isArrayType(containerType)) {
+ return TypeUtils.getArrayComponentType(containerType);
+ }
+ if (TypeUtils.isAssignable(containerType, Iterable.class)) {
+ Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerType, Iterable.class);
+ return typeArguments.containsKey(ITERABLE_TYPE) ? typeArguments.get(ITERABLE_TYPE) : Object.class;
+ }
+ return null;
+ }
+
+ private Type containerType;
+ private Integer index;
+
+ /**
+ * Create a new IndexedAccessStrategy instance.
+ *
+ * @param containerType
+ * @param index
+ */
+ public IndexedAccess(Type containerType, Integer index) {
+ super();
+ this.containerType = containerType;
+ this.index = index;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(Object instance) {
+ if (index == null) {
+ throw new UnsupportedOperationException("Cannot read null index");
+ }
+ if (instance != null && instance.getClass().isArray()) {
+ if (Array.getLength(instance) - index > 0) {
+ return Array.get(instance, index);
+ }
+ } else if (instance instanceof List<?>) {
+ List<?> list = (List<?>) instance;
+ if (list.size() - index > 0) {
+ return list.get(index);
+ }
+ } else if (instance instanceof Iterable<?>) {
+ int i = 0;
+ for (Object o : (Iterable<?>) instance) {
+ if (++i == index) {
+ return o;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ElementType getElementType() {
+ return ElementType.METHOD;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getJavaType() {
+ return getJavaElementType(containerType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPropertyName() {
+ return String.format("[%d]", index);
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/KeyedAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/KeyedAccess.java
new file mode 100644
index 0000000..6050502
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/KeyedAccess.java
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Map;
+
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * {@link AccessStrategy} to get a keyed value from a {@link Map}. Contains
+ * special handling when a string key is used against a container type whose key
+ * parameter is not assignable from {@link String}: against a map whose key type
+ * is an enum class, it will be interpreted as a named enum constant; other key
+ * types will be compared via {@link Object#toString()}.
+ */
+public class KeyedAccess extends AccessStrategy {
+ private static final TypeVariable<?>[] MAP_TYPEVARS = Map.class.getTypeParameters();
+
+ /**
+ * Get the Java element type of a particular container type.
+ *
+ * @param containerType
+ * @return Type or <code>null</code> if <code>containerType</code> is not
+ * some kind of {@link Map}
+ */
+ public static Type getJavaElementType(Type containerType) {
+ if (TypeUtils.isAssignable(containerType, Map.class)) {
+ Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerType, Map.class);
+ return typeArguments.containsKey(MAP_TYPEVARS[1]) ? typeArguments.get(MAP_TYPEVARS[1]) : Object.class;
+ }
+ return null;
+ }
+
+ private Type containerType;
+ private Object key;
+
+ /**
+ * Create a new KeyedAccess instance.
+ *
+ * @param containerType
+ * @param key
+ */
+ public KeyedAccess(Type containerType, Object key) {
+ super();
+ this.containerType = containerType;
+ this.key = key;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(Object instance) {
+ if (instance instanceof Map<?, ?>) {
+ Map<?, ?> map = (Map<?, ?>) instance;
+ Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerType, Map.class);
+ Type keyType = typeArguments.get(MAP_TYPEVARS[0]);
+ if (key == null || keyType == null || TypeUtils.isInstance(key, keyType)) {
+ return map.get(key);
+ }
+ if (key instanceof String) {
+ String name = (String) key;
+ Class<?> rawKeyType = TypeUtils.getRawType(keyType, containerType);
+ if (rawKeyType.isEnum()) {
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final Object result = map.get(Enum.valueOf((Class<? extends Enum>) rawKeyType, name));
+ return result;
+ }
+ for (Map.Entry<?, ?> e : map.entrySet()) {
+ if (name.equals(e.getKey())) {
+ return e.getValue();
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ElementType getElementType() {
+ return ElementType.METHOD;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getJavaType() {
+ Type result = getJavaElementType(containerType);
+ return result == null ? Object.class : result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPropertyName() {
+ return String.format("[%s]", key);
+ }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/MethodAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/MethodAccess.java
new file mode 100644
index 0000000..2583fbb
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/MethodAccess.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import java.beans.Introspector;
+import java.lang.annotation.ElementType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Description: invoke a zero-argument method (getter)<br/>
+ */
+public class MethodAccess extends AccessStrategy {
+ private final Method method;
+ private final String propertyName;
+
+ /**
+ * Create a new MethodAccess instance.
+ * @param method
+ */
+ public MethodAccess(Method method) {
+ this(getPropertyName(method), method);
+ }
+
+ /**
+ * Create a new MethodAccess instance.
+ * @param propertyName
+ * @param method
+ */
+ public MethodAccess(String propertyName, final Method method) {
+ this.method = method;
+ this.propertyName = propertyName;
+ if (!method.isAccessible()) {
+ run( new PrivilegedAction<Void>() {
+ public Void run() {
+ method.setAccessible(true);
+ return null;
+ }
+ });
+ }
+ }
+
+ /**
+ * Process bean properties getter by applying the JavaBean naming conventions.
+ *
+ * @param member the member for which to get the property name.
+ * @return The bean method name with the "is" or "get" prefix stripped off, <code>null</code>
+ * the method name id not according to the JavaBeans standard.
+ */
+ public static String getPropertyName(Method member) {
+ String name = null;
+ String methodName = member.getName();
+ if (methodName.startsWith("is")) {
+ name = Introspector.decapitalize(methodName.substring(2));
+ } /* else if ( methodName.startsWith("has")) {
+ name = Introspector.decapitalize( methodName.substring( 3 ) );
+ } */
+ // setter annotation is NOT supported in the spec
+ /* else if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
+ propName = Introspector.decapitalize(method.getName().substring(3));
+ } */
+ else if (methodName.startsWith("get")) {
+ name = Introspector.decapitalize(methodName.substring(3));
+ }
+ return name;
+ }
+
+ /**
+ * {@inheritDoc}
+ * normally the propertyName of the getter method, e.g.<br>
+ * method: getName() -> propertyName: name<br>
+ * method: isValid() -> propertyName: valid<br>
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object get(final Object instance) {
+ try {
+ return method.invoke(instance);
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ElementType getElementType() {
+ return ElementType.METHOD;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Type getJavaType() {
+ return method.getGenericReturnType();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return method.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodAccess that = (MethodAccess) o;
+
+ return method.equals(that.method);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int hashCode() {
+ return method.hashCode();
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/PrivilegedActions.java b/trunk/bval-core/src/main/java/org/apache/bval/util/PrivilegedActions.java
new file mode 100644
index 0000000..26c0936
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/PrivilegedActions.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import org.apache.commons.lang3.ClassUtils;
+
+/**
+ * Description: utility methods to perform actions with AccessController or without. <br/>
+ */
+public class PrivilegedActions {
+ private static String lineSeparator = null;
+ private static String pathSeparator = null;
+
+ /**
+ * Return the value of the "line.separator" system property.
+ *
+ * Requires security policy:
+ * 'permission java.util.PropertyPermission "read";'
+ */
+ @Deprecated // unused method - will remove in future release
+ public static final String getLineSeparator() {
+ if (lineSeparator == null) {
+ lineSeparator =
+ AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("line.separator");
+ }
+ });
+ }
+ return lineSeparator;
+ }
+
+ /**
+ * Return the value of the "path.separator" system property.
+ *
+ * Requires security policy:
+ * 'permission java.util.PropertyPermission "read";'
+ */
+ @Deprecated // unused method - will remove in future release
+ public static final String getPathSeparator() {
+ if (pathSeparator == null) {
+ pathSeparator =
+ AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("path.separator");
+ }
+ });
+ }
+ return pathSeparator;
+ }
+
+ /**
+ * Perform action with AccessController.doPrivileged() if security if enabled.
+ *
+ * @param action - the action to run
+ * @return result of running the action
+ */
+ // should not be called by just anyone; do not increase access
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+ /**
+ * Perform action with AccessController.doPrivileged() if security if enabled.
+ *
+ * @param action - the action to run
+ * @return result of running the action
+ */
+ // should not be called by just anyone; do not increase access
+ private static <T> T run(final PrivilegedExceptionAction<T> action) throws PrivilegedActionException, Exception {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+ /**
+ * Perform AccessController.doPrivileged() action for ClassUtil.getClass()
+ *
+ * @return Class
+ * @exception Exception
+ */
+ public static Class<?> getClass(final ClassLoader classLoader, final String className) throws Exception {
+ return run(new PrivilegedExceptionAction<Class<?>>() {
+ public Class<?> run() throws Exception {
+ return ClassUtils.getClass(classLoader, className, true);
+ }
+ });
+ }
+
+ /**
+ * Return a PrivilegedAction object for clazz.getDeclaredMethod().invoke().
+ *
+ * Requires security policy
+ * 'permission java.lang.RuntimePermission "accessDeclaredMembers";'
+ * 'permission java.lang.reflect.ReflectPermission "suppressAccessChecks";'
+ *
+ * @return Object
+ * @exception IllegalAccessException, InvocationTargetException
+ */
+ public static Object getAnnotationValue(final Annotation annotation, final String name)
+ throws IllegalAccessException, InvocationTargetException {
+ return run(new PrivilegedAction<Object>() {
+ public Object run() {
+ Method valueMethod;
+ try {
+ valueMethod = annotation.annotationType().getDeclaredMethod(name);
+ } catch (NoSuchMethodException ex) {
+ // do nothing
+ valueMethod = null;
+ }
+ if (null != valueMethod) {
+ try {
+ valueMethod.setAccessible(true);
+ return valueMethod.invoke(annotation);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Return a PrivilegeAction object for clazz.getClassloader().
+ *
+ * Requires security policy:
+ * 'permission java.lang.RuntimePermission "getClassLoader";'
+ *
+ * @return Classloader
+ */
+ public static ClassLoader getClassLoader(final Class<?> clazz) {
+ return run(new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null) {
+ cl = clazz.getClassLoader();
+ }
+ return cl;
+ }
+ });
+ }
+
+ /**
+ * Return a PrivilegeAction object for System.getProperty().
+ *
+ * Requires security policy:
+ * 'permission java.util.PropertyPermission "read";'
+ *
+ * @return String
+ */
+ public static final String getProperty(final String name) {
+ return run(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty(name);
+ }
+ });
+ }
+
+}
+
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java
new file mode 100644
index 0000000..d79235e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.util;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ * Description: Undefined dynamic strategy (FIELD or METHOD access) Uses PropertyUtils or tries to determine field to
+ * access the value<br/>
+ */
+public class PropertyAccess extends AccessStrategy {
+ private final Class<?> beanClass;
+ private final String propertyName;
+ private Field rememberField;
+
+ /**
+ * Create a new PropertyAccess instance.
+ *
+ * @param clazz
+ * @param propertyName
+ */
+ public PropertyAccess(Class<?> clazz, String propertyName) {
+ this.beanClass = clazz;
+ this.propertyName = propertyName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ElementType getElementType() {
+ return rememberField != null ? ElementType.FIELD : ElementType.METHOD;
+ }
+
+ private static Object getPublicProperty(Object bean, String property) throws InvocationTargetException,
+ NoSuchMethodException, IllegalAccessException {
+ if (bean instanceof Map<?, ?>) {
+ return ((Map<?, ?>) bean).get(property);
+ } else { // supports DynaBean and standard Objects
+ return PropertyUtils.getSimpleProperty(bean, property);
+ }
+ }
+
+ /**
+ * Get a named property from <code>bean</code>.
+ *
+ * @param bean
+ * @param propertyName
+ * @return Object found
+ * @throws InvocationTargetException
+ * @throws NoSuchMethodException
+ * @throws IllegalAccessException
+ */
+ public static Object getProperty(Object bean, String propertyName) throws InvocationTargetException,
+ NoSuchMethodException, IllegalAccessException {
+ return new PropertyAccess(bean.getClass(), propertyName).get(bean);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "Property{" + beanClass.getName() + '.' + propertyName + '}';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Type getJavaType() {
+ Type result = getTypeInner();
+ return result == null ? Object.class : result;
+ }
+
+ /**
+ * Learn whether this {@link PropertyAccess} references a known property.
+ *
+ * @return boolean
+ */
+ public boolean isKnown() {
+ return getTypeInner() != null;
+ }
+
+ /**
+ * Find out what, if any, type can be calculated.
+ *
+ * @return type found or <code>null</code>
+ */
+ private Type getTypeInner() {
+ if (rememberField != null) {
+ return rememberField.getGenericType();
+ }
+ Method readMethod = getPropertyReadMethod(propertyName, beanClass);
+ if (readMethod != null) {
+ return readMethod.getGenericReturnType();
+ }
+ Field fld = getField(propertyName, beanClass);
+ if (fld != null) {
+ cacheField(fld);
+ return rememberField.getGenericType();
+ }
+ return null;
+ }
+
+ private static Method getPropertyReadMethod(String propertyName, Class<?> beanClass) {
+ for (PropertyDescriptor each : PropertyUtils.getPropertyDescriptors(beanClass)) {
+ if (each.getName().equals(propertyName)) {
+ return each.getReadMethod();
+ }
+ }
+ return null;
+ }
+
+ private static Field getField(String propertyName, Class<?> beanClass) {
+ try { // try public field
+ return beanClass.getField(propertyName);
+ } catch (NoSuchFieldException ex2) {
+ // search for private/protected field up the hierarchy
+ Class<?> theClass = beanClass;
+ while (theClass != null) {
+ try {
+ return theClass.getDeclaredField(propertyName);
+ } catch (NoSuchFieldException ex3) {
+ // do nothing
+ }
+ theClass = theClass.getSuperclass();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object get(Object bean) {
+ try {
+ if (rememberField != null) { // cache field of previous access
+ return rememberField.get(bean);
+ }
+ try { // try public method
+ return getPublicProperty(bean, propertyName);
+ } catch (NoSuchMethodException ex) {
+ return getFieldValue(bean);
+ }
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IllegalArgumentException("cannot access " + propertyName, e);
+ }
+ }
+
+ private Object getFieldValue(Object bean) throws IllegalAccessException {
+ Field field = getField(propertyName, beanClass);
+ if (field != null) {
+ cacheField(field);
+ return rememberField.get(bean);
+ }
+ throw new IllegalArgumentException("cannot access field " + propertyName);
+ }
+
+ private void cacheField(Field field) {
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ this.rememberField = field;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ PropertyAccess that = (PropertyAccess) o;
+
+ return beanClass.equals(that.beanClass) && propertyName.equals(that.propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int hashCode() {
+ int result;
+ result = beanClass.hashCode();
+ result = 31 * result + propertyName.hashCode();
+ return result;
+ }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/ValidationHelper.java b/trunk/bval-core/src/main/java/org/apache/bval/util/ValidationHelper.java
new file mode 100644
index 0000000..35677c8
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/ValidationHelper.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.util;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+
+/**
+ * Stateless helper methods used by the validators.
+ *
+ * @author Carlos Vara
+ */
+public class ValidationHelper {
+
+ /**
+ * Interface implemented by the call-back object passed to
+ * {@link ValidationHelper#validateContext(ValidationContext, ValidateCallback, boolean)}
+ * . Its {@link #validate()} method will be called accordingly for every
+ * dispatch.
+ */
+ public static interface ValidateCallback {
+ void validate();
+ }
+
+ /**
+ * validate a complex 'bean' with related beans according to
+ * validation rules in 'metaBean'
+ *
+ * @param context
+ * - the context is initialized with: <br>
+ * bean - the root object start validation at
+ * or a collection of root objects <br>
+ * metaBean - the meta information for the root
+ * object(s)
+ * @param context
+ * The current validation context.
+ */
+ static public void validateContext(ValidationContext<?> context, ValidateCallback s, boolean treatMapsLikeBeans) {
+ if (context.getBean() != null) {
+ if (!treatMapsLikeBeans && context.getBean() instanceof Map<?, ?>) {
+ validateMapInContext(context, s);
+ } else if (context.getBean() instanceof Iterable<?>) {
+ validateIterableInContext(context, s);
+ } else if (context.getBean() instanceof Object[]) {
+ validateArrayInContext(context, s);
+ } else { // to One Bean (or Map like Bean)
+ validateBeanInContext(context, s);
+ }
+ }
+ }
+
+ /**
+ * Validates a single object.
+ *
+ * @param <VL>
+ * @param context
+ * The validation context, its current bean must be a single
+ * object.
+ * @param s
+ */
+ static protected <VL extends ValidationListener> void validateBeanInContext(ValidationContext<VL> context, ValidateCallback s) {
+ if (getDynamicMetaBean(context) != null) {
+ context.setMetaBean(getDynamicMetaBean(context).resolveMetaBean(context.getBean()));
+ }
+ s.validate();
+ }
+
+ /**
+ * Iterates the values of an array, setting the current context
+ * appropriately and validating each value.
+ *
+ * @param <VL>
+ * @param context
+ * The validation context, its current bean must be an array.
+ */
+ static protected <VL extends ValidationListener> void validateArrayInContext(ValidationContext<VL> context, ValidateCallback s) {
+ int index = 0;
+ DynamicMetaBean dyn = getDynamicMetaBean(context);
+ Object[] array = (Object[]) context.getBean();
+ MetaBean metaBean = context.getMetaBean();
+ context.setCurrentIndex(null);
+ try {
+ for (Object each : array) {
+ context.setCurrentIndex(index++);
+ if (each == null) {
+ continue; // Null values are not validated
+ }
+ if (dyn != null) {
+ context.setBean(each, dyn.resolveMetaBean(each));
+ } else {
+ context.setBean(each);
+ }
+ s.validate();
+ }
+ } finally {
+ context.moveUp(array, metaBean);
+ }
+ }
+
+ /**
+ * Iterates the values of an {@link Iterable} object, setting the current
+ * context appropriately and validating each value.
+ *
+ * @param <VL>
+ * @param context
+ * The validation context, its current bean must implement
+ * {@link Iterable}.
+ */
+ static protected <VL extends ValidationListener> void validateIterableInContext(ValidationContext<VL> context, ValidateCallback s) {
+
+ final boolean positional = context.getBean() instanceof List<?>;
+ int index = 0;
+ Iterable<?> iterable = (Iterable<?>) context.getBean();
+ MetaBean metaBean = context.getMetaBean();
+ context.setCurrentIndex(null);
+
+ try {
+ // jsr303 spec: Each object provided by the iterator is validated.
+ final DynamicMetaBean dyn = getDynamicMetaBean(context);
+ for (Object each : iterable) {
+ if (positional) {
+ context.setCurrentIndex(index++);
+ }
+ if (each == null) {
+ continue; // Null values are not validated
+ }
+ if (dyn != null) {
+ context.setBean(each, dyn.resolveMetaBean(each));
+ } else {
+ context.setBean(each);
+ }
+ s.validate();
+ }
+ } finally {
+ context.moveUp(iterable, metaBean);
+ }
+ }
+
+ /**
+ * Iterates the values of a {@link Map}, setting the current context
+ * appropriately and validating each value.
+ *
+ * @param <VL>
+ * @param context
+ * The validation context, its current bean must implement
+ * {@link Map}.
+ */
+ static protected <VL extends ValidationListener> void validateMapInContext(ValidationContext<VL> context, ValidateCallback s) {
+ // jsr303 spec: For Map, the value of each Map.Entry is validated (key
+ // is not validated).
+ Map<?, ?> currentBean = (Map<?, ?>) context.getBean();
+ MetaBean metaBean = context.getMetaBean();
+ final DynamicMetaBean dyn = getDynamicMetaBean(context);
+ context.setCurrentKey(null);
+ try {
+ for (Map.Entry<?, ?> entry : currentBean.entrySet()) {
+ Object value = entry.getValue();
+ if (value == null) {
+ continue;
+ }
+ context.setCurrentKey(entry.getKey());
+ if (dyn == null) {
+ context.setBean(value);
+ } else {
+ context.setBean(value, dyn.resolveMetaBean(value));
+ }
+ s.validate();
+ }
+ } finally {
+ context.moveUp(currentBean, metaBean);
+ }
+ }
+
+ /**
+ * @param <VL>
+ * @param context
+ * The current validation context.
+ * @return the current {@link DynamicMetaBean} in context, or
+ * <code>null</code> if the current meta bean is not dynamic.
+ */
+ static private <VL extends ValidationListener> DynamicMetaBean getDynamicMetaBean(ValidationContext<VL> context) {
+ return context.getMetaBean() instanceof DynamicMetaBean ? (DynamicMetaBean) context.getMetaBean() : null;
+ }
+
+
+ /**
+ * Validate a single bean only, no related beans will be validated.
+ */
+ static public <VL extends ValidationListener> void validateBean(ValidationContext<VL> context) {
+ // execute all property level validations
+ for (MetaProperty prop : context.getMetaBean().getProperties()) {
+ context.setMetaProperty(prop);
+ validateProperty(context);
+ }
+
+ // execute all bean level validations
+ context.setMetaProperty(null);
+ for (Validation validation : context.getMetaBean().getValidations()) {
+ validation.validate(context);
+ }
+ }
+
+
+ /**
+ * Validate a single property only. Performs all validations
+ * for this property.
+ */
+ static public <VL extends ValidationListener> void validateProperty(ValidationContext<VL> context) {
+ for (Validation validation : context.getMetaProperty().getValidations()) {
+ validation.validate(context);
+ }
+ }
+}
diff --git a/trunk/bval-core/src/test/java/org/apache/bval/ValidationResultsTest.java b/trunk/bval-core/src/test/java/org/apache/bval/ValidationResultsTest.java
new file mode 100644
index 0000000..e16f9de
--- /dev/null
+++ b/trunk/bval-core/src/test/java/org/apache/bval/ValidationResultsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.model.MetaProperty;
+
+/**
+ * ValidationResults Tester.
+ */
+public class ValidationResultsTest extends TestCase {
+ private ValidationResults results;
+
+ public ValidationResultsTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ results = new ValidationResults();
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testValidationResults() throws Exception {
+ assertTrue(results.isEmpty());
+ BeanValidationContext<ValidationResults> ctx = new BeanValidationContext<ValidationResults>(results);
+ ctx.setBean(this);
+ ctx.setMetaProperty(new MetaProperty());
+ ctx.getMetaProperty().setName("prop");
+ results.addError("test", ctx);
+ assertFalse(results.isEmpty());
+ assertTrue(results.hasErrorForReason("test"));
+ assertTrue(results.hasError(this, "prop"));
+ assertTrue(results.hasError(this, null));
+ assertFalse(results.hasError(this, "prop2"));
+ }
+
+ public static Test suite() {
+ return new TestSuite(ValidationResultsTest.class);
+ }
+}
diff --git a/trunk/bval-core/src/test/java/org/apache/bval/model/ExampleEnum.java b/trunk/bval-core/src/test/java/org/apache/bval/model/ExampleEnum.java
new file mode 100644
index 0000000..10f87a9
--- /dev/null
+++ b/trunk/bval-core/src/test/java/org/apache/bval/model/ExampleEnum.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 18:00:12<br>
+ */
+public enum ExampleEnum {
+ VALUE1, VALUE2, VALUE3
+}
diff --git a/trunk/bval-core/src/test/java/org/apache/bval/model/MetaPropertyTest.java b/trunk/bval-core/src/test/java/org/apache/bval/model/MetaPropertyTest.java
new file mode 100644
index 0000000..35b626a
--- /dev/null
+++ b/trunk/bval-core/src/test/java/org/apache/bval/model/MetaPropertyTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.model;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * MetaProperty Tester.
+ *
+ * @author <Authors name>
+ * @since <pre>02/12/2009</pre>
+ * @version 1.0
+ */
+public class MetaPropertyTest extends TestCase {
+ public MetaPropertyTest(String name) {
+ super(name);
+ }
+
+
+ public void testGetTypeClass() throws Exception {
+ MetaProperty prop = new MetaProperty();
+ prop.setType(String.class);
+ assertEquals(String.class, prop.getTypeClass());
+ assertEquals(String.class, prop.getType());
+ prop.setType(new DynaTypeEnum(ExampleEnum.class, ExampleEnum.VALUE1.name(),
+ ExampleEnum.VALUE3.name()));
+ assertEquals(ExampleEnum.class, prop.getTypeClass());
+ assertEquals(2, ((DynaTypeEnum)prop.getType()).getEnumConstants().length);
+ }
+
+
+ public static Test suite() {
+ return new TestSuite(MetaPropertyTest.class);
+ }
+}
diff --git a/trunk/bval-core/src/test/resources/log4j.xml b/trunk/bval-core/src/test/resources/log4j.xml
new file mode 100644
index 0000000..3c4bb67
--- /dev/null
+++ b/trunk/bval-core/src/test/resources/log4j.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<!-- ===================================================================== -->
+<!-- -->
+<!-- Log4j Configuration -->
+<!-- -->
+<!-- ===================================================================== -->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <param name="Threshold" value="DEBUG"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
+ </layout>
+ </appender>
+
+ <root>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+</log4j:configuration>
diff --git a/trunk/bval-extras/pom.xml b/trunk/bval-extras/pom.xml
new file mode 100644
index 0000000..bab695f
--- /dev/null
+++ b/trunk/bval-extras/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-extras</artifactId>
+ <name>Apache BVal :: bval-extras (optional)</name>
+ <packaging>bundle</packaging>
+
+ <description>BVal - non-JSR303 routines and constraints</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>org.apache.bval.bundle</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <!--
+ default profile using geronimo-validation_1.0_spec.jar
+ active when property "ri" is not present.
+ -->
+ <profile>
+ <id>geronimo</id>
+ <activation>
+ <property>
+ <name>!ri</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-validation_1.0_spec</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <!--
+ optional profile using javax.validation/validation-api.jar
+ from RI manually active when property "-Pri" is provided.
+ -->
+ <profile>
+ <id>ri</id>
+ <activation>
+ <property>
+ <name>ri</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
new file mode 100644
index 0000000..7e48693
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = ABANumberValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface ABANumber {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.ABANumber.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidator.java
new file mode 100644
index 0000000..4eed858
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidator.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 10 <b>ABA Number</b> (or <b>Routing Transit Number</b> (RTN)) Check Digit
+ * calculation/validation.
+ * <p>
+ * ABA Numbers (or Routing Transit Numbers) are a nine digit numeric code used
+ * to identify American financial institutions for things such as checks or deposits
+ * (ABA stands for the American Bankers Association).
+ * <p>
+ * Check digit calculation is based on <i>modulus 10</i> with digits being weighted
+ * based on their position (from right to left) as follows:
+ * <ul>
+ * <li>Digits 1, 4 and & 7 are weighted 1
+ * <li>Digits 2, 5 and & 8 are weighted 7
+ * <li>Digits 3, 6 and & 9 are weighted 3
+ * </ul>
+ * <p>
+ * For further information see
+ * <a href="http://en.wikipedia.org/wiki/Routing_transit_number">Wikipedia -
+ * Routing transit number</a>.
+ */
+public final class ABANumberValidator
+ extends ModulusValidator<ABANumber> {
+
+ /** weighting given to digits depending on their right position */
+ private static final int[] POSITION_WEIGHT = new int[] {3, 1, 7};
+
+ public ABANumberValidator() {
+ super(10);
+ }
+
+ /**
+ * Calculates the <i>weighted</i> value of a character in the
+ * code at a specified position.
+ * <p>
+ * ABA Routing numbers are weighted in the following manner:
+ * <pre><code>
+ * left position: 1 2 3 4 5 6 7 8 9
+ * weight: 3 7 1 3 7 1 3 7 1
+ * </code></pre>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected int weightedValue( int charValue, int leftPos, int rightPos )
+ throws Exception {
+ int weight = POSITION_WEIGHT[rightPos % 3];
+ return (charValue * weight);
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
new file mode 100644
index 0000000..860b7d3
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = CUSIPValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface CUSIP {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.CUSIP.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidator.java
new file mode 100644
index 0000000..82e8c29
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidator.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+/**
+ * Modulus 10 <b>CUSIP</b> (North American Securities)
+ * Check Digit calculation/validation.
+ * <p>
+ * CUSIP Numbers are 9 character alphanumeric codes used
+ * to identify North American Securities.
+ * <p>
+ * Check digit calculation uses the <i>Modulus 10 Double Add Double</i> technique
+ * with every second digit being weighted by 2. Alphabetic characters are
+ * converted to numbers by their position in the alphabet starting with A being 10.
+ * Weighted numbers greater than ten are treated as two separate numbers.
+ * <p>
+ *
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/CUSIP">Wikipedia - CUSIP</a>
+ * for more details.
+ */
+public final class CUSIPValidator
+ extends ModulusValidator<CUSIP> {
+
+ /** weighting given to digits depending on their right position */
+ private static final int[] POSITION_WEIGHT = new int[] {2, 1};
+
+ public CUSIPValidator() {
+ super(10);
+ }
+
+ /**
+ * Calculates the <i>weighted</i> value of a character in the
+ * code at a specified position.
+ * <p>
+ * ABA Routing numbers are weighted in the following manner:
+ * <pre><code>
+ * left position: 1 2 3 4 5 6 7 8 9
+ * weight: 3 7 1 3 7 1 3 7 1
+ * </code></pre>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected int weightedValue( int charValue, int leftPos, int rightPos )
+ throws Exception {
+ int weight = POSITION_WEIGHT[rightPos % 2];
+ int weightedValue = (charValue * weight);
+ return sumDigits(weightedValue);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected int toInt(char character, int leftPos, int rightPos) {
+ int charValue = getNumericValue(character);
+ if (charValue < 0 || charValue > 35) {
+ throw new IllegalArgumentException("Invalid Character["
+ + leftPos
+ + "] = '"
+ + charValue
+ + "'");
+ }
+ return charValue;
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
new file mode 100644
index 0000000..d72af8f
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = EAN13Validator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface EAN13 {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.EAN13.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13Validator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13Validator.java
new file mode 100644
index 0000000..4270394
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13Validator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 10 <b>EAN-13</b> / <b>UPC</b> / <b>ISBN-13</b> Check Digit
+ * calculation/validation.
+ * <p>
+ * Check digit calculation is based on <i>modulus 10</i> with digits in
+ * an <i>odd</i> position (from right to left) being weighted 1 and <i>even</i>
+ * position digits being weighted 3.
+ * <p>
+ * For further information see:
+ * <ul>
+ * <li>EAN-13 - see
+ * <a href="http://en.wikipedia.org/wiki/European_Article_Number">Wikipedia -
+ * European Article Number</a>.</li>
+ * <li>UPC - see
+ * <a href="http://en.wikipedia.org/wiki/Universal_Product_Code">Wikipedia -
+ * Universal Product Code</a>.</li>
+ * <li>ISBN-13 - see
+ * <a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International
+ * Standard Book Number (ISBN)</a>.</li>
+ * </ul>
+ */
+public final class EAN13Validator
+ extends ModulusValidator<EAN13> {
+
+ /** weighting given to digits depending on their right position */
+ private static final int[] POSITION_WEIGHT = new int[] {3, 1};
+
+ public EAN13Validator() {
+ super(10);
+ }
+
+ /**
+ * <p>Calculates the <i>weighted</i> value of a character in the
+ * code at a specified position.</p>
+ *
+ * <p>For EAN-13 (from right to left) <b>odd</b> digits are weighted
+ * with a factor of <b>one</b> and <b>even</b> digits with a factor
+ * of <b>three</b>.</p>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected int weightedValue( int charValue, int leftPos, int rightPos )
+ throws Exception {
+ int weight = POSITION_WEIGHT[rightPos % 2];
+ return (charValue * weight);
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
new file mode 100644
index 0000000..c2f33c8
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = IBANValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface IBAN {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.IBAN.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
new file mode 100644
index 0000000..e365ce1
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <b>IBAN</b> (International Bank Account Number) Check Digit calculation/validation.
+ * <p>
+ * This rountine is based on the ISO 7064 Mod 97,10 check digit caluclation routine.
+ * <p>
+ * The two check digit characters in a IBAN number are the third and fourth characters
+ * in the code. For <i>check digit</i> calculation/validation the first four characters are moved
+ * to the end of the code.
+ * So <code>CCDDnnnnnnn</code> becomes <code>nnnnnnnCCDD</code> (where
+ * <code>CC</code> is the country code and <code>DD</code> is the check digit). For
+ * check digit calcualtion the check digit value should be set to zero (i.e.
+ * <code>CC00nnnnnnn</code> in this example.
+ * <p>
+ * For further information see
+ * <a href="http://en.wikipedia.org/wiki/International_Bank_Account_Number">Wikipedia -
+ * IBAN number</a>.
+ */
+public final class IBANValidator
+ implements ConstraintValidator<IBAN, String> {
+
+ private static final long MAX = 999999999;
+
+ private static final long MODULUS = 97;
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(String code, ConstraintValidatorContext context) {
+ if (code.length() < 5) {
+ return false;
+ }
+
+ String reformattedCode = code.substring(4) + code.substring(0, 4);
+ long total = 0;
+ for (int i = 0; i < reformattedCode.length(); i++) {
+ int charValue = getNumericValue(reformattedCode.charAt(i));
+ if (charValue < 0 || charValue > 35) {
+ return false;
+ }
+ total = (charValue > 9 ? total * 100 : total * 10) + charValue;
+ if (total > MAX) {
+ total = (total % MODULUS);
+ }
+ }
+
+ return (total % MODULUS) == 1;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize( IBAN iban ) {
+ // not needed
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
new file mode 100644
index 0000000..31aab70
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = ISBN10Validator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface ISBN10 {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.ISBN10.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10Validator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10Validator.java
new file mode 100644
index 0000000..cf2c133
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10Validator.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 11 <b>ISBN-10</b> Check Digit calculation/validation.
+ * <p>
+ * ISBN-10 Numbers are a numeric code except for the last (check) digit
+ * which can have a value of "X".
+ * <p>
+ * Check digit calculation is based on <i>modulus 11</i> with digits being weighted
+ * based by their position, from right to left with the first digit being weighted
+ * 1, the second 2 and so on. If the check digit is calculated as "10" it is converted
+ * to "X".
+ * <p>
+ * <b>N.B.</b> From 1st January 2007 the book industry will start to use a new 13 digit
+ * ISBN number (rather than this 10 digit ISBN number) which uses the EAN-13 / UPC
+ * (see {@link EAN13CheckDigit}) standard.
+ * <p>
+ * For further information see:
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International
+ * Standard Book Number (ISBN)</a>.</li>
+ * <li><a href="http://www.isbn.org/standards/home/isbn/transition.asp">ISBN-13
+ * Transition details</a>.</li>
+ * </ul>
+ */
+public final class ISBN10Validator
+ extends ModulusValidator<ISBN10> {
+
+ public ISBN10Validator() {
+ super(11);
+ }
+
+ /**
+ * Calculates the <i>weighted</i> value of a charcter in the
+ * code at a specified position.
+ *
+ * <p>For ISBN-10 (from right to left) digits are weighted
+ * by their position.</p>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected int weightedValue( int charValue, int leftPos, int rightPos )
+ throws Exception {
+ return (charValue * rightPos);
+ }
+
+ /**
+ * <p>Convert a character at a specified position to an
+ * integer value.</p>
+ *
+ * <p>Character 'X' check digit converted to 10.</p>
+ *
+ * {@inheritDoc}
+ */
+ protected int toInt(char character, int leftPos, int rightPos) {
+ if (rightPos == 1 && character == 'X') {
+ return 10;
+ }
+ return super.toInt(character, leftPos, rightPos);
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
new file mode 100644
index 0000000..03be6ca
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = LuhnValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Luhn {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.Luhn.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/LuhnValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/LuhnValidator.java
new file mode 100644
index 0000000..030fe3c
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/LuhnValidator.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 10 <b>Luhn</b> Check Digit calculation/validation.
+ * <p>
+ * Luhn check digits are used, for example, by:
+ * <ul>
+ * <li><a href="http://en.wikipedia.org/wiki/Credit_card">Credit Card Numbers</a></li>
+ * <li><a href="http://en.wikipedia.org/wiki/IMEI">IMEI Numbers</a> - International
+ * Mobile Equipment Identity Numbers</li>
+ * </ul>
+ * Check digit calculation is based on <i>modulus 10</i> with digits in
+ * an <i>odd</i> position (from right to left) being weighted 1 and <i>even</i>
+ * position digits being weighted 2 (weighted values greater than 9 have 9 subtracted).
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/Luhn_algorithm">Wikipedia</a>
+ * for more details.
+ */
+public final class LuhnValidator
+ extends ModulusValidator<Luhn> {
+
+ /** weighting given to digits depending on their right position */
+ private static final int[] POSITION_WEIGHT = new int[] {2, 1};
+
+ public LuhnValidator() {
+ super(10);
+ }
+
+ /**
+ * <p>Calculates the <i>weighted</i> value of a charcter in the
+ * code at a specified position.</p>
+ *
+ * <p>For Luhn (from right to left) <b>odd</b> digits are weighted
+ * with a factor of <b>one</b> and <b>even</b> digits with a factor
+ * of <b>two</b>. Weighted values > 9, have 9 subtracted</p>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ protected int weightedValue( int charValue, int leftPos, int rightPos )
+ throws Exception {
+ int weight = POSITION_WEIGHT[rightPos % 2];
+ int weightedValue = (charValue * weight);
+ return (weightedValue > 9 ? (weightedValue - 9) : weightedValue);
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
new file mode 100644
index 0000000..aeb7249
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+import static java.lang.Character.isDigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Abstract <b>Modulus</b> Check digit calculation/validation.
+ * <p>
+ * Provides a <i>base</i> class for building <i>modulus</i> Check
+ * Digit routines.
+ * <p>
+ * This implementation only handles <i>numeric</i> codes, such as
+ * <b>EAN-13</b>. For <i>alphanumeric</i> codes such as <b>EAN-128</b> you
+ * will need to implement/override the <code>toInt()</code> and
+ * <code>toChar()</code> methods.
+ *
+ * @param <A>
+ */
+abstract class ModulusValidator<A extends Annotation>
+ implements ConstraintValidator<A, String> {
+
+ private final int modulus;
+
+ public ModulusValidator(int modulus) {
+ this.modulus = modulus;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public final void initialize(A annotation) {
+ // not needed ATM
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(String code, ConstraintValidatorContext context) {
+ if (code.length() == 0) {
+ return false;
+ }
+ int total = 0;
+ for (int i = 0; i < code.length(); i++) {
+ int lth = code.length();
+ int leftPos = i + 1;
+ int rightPos = lth - i;
+ try {
+ int charValue = toInt(code.charAt(i), leftPos, rightPos);
+ total += weightedValue(charValue, leftPos, rightPos);
+ } catch (Throwable e) {
+ return false;
+ }
+ }
+ if (total == 0) {
+ return false;
+ }
+ return (total % modulus) == 0;
+ }
+
+ /**
+ * Calculates the <i>weighted</i> value of a character in the
+ * code at a specified position.
+ * <p>
+ * Some modulus routines weight the value of a character
+ * depending on its position in the code (e.g. ISBN-10), while
+ * others use different weighting factors for odd/even positions
+ * (e.g. EAN or Luhn). Implement the appropriate mechanism
+ * required by overriding this method.
+ *
+ * @param charValue The numeric value of the character
+ * @param leftPos The position of the character in the code, counting from left to right
+ * @param rightPos The position of the character in the code, counting from right to left
+ * @return The weighted value of the character
+ */
+ protected abstract int weightedValue(int charValue, int leftPos, int rightPos)
+ throws Exception;
+
+ /**
+ * Convert a character at a specified position to an integer value.
+ * <p>
+ * <b>Note:</b> this implementation only handlers numeric values
+ * For non-numeric characters, override this method to provide
+ * character-->integer conversion.
+ *
+ * @param character The character to convert
+ * @param leftPos The position of the character in the code, counting from left to right
+ * @param rightPos The positionof the character in the code, counting from right to left
+ * @return The integer value of the character
+ */
+ protected int toInt(char character, int leftPos, int rightPos) {
+ if (isDigit(character)) {
+ return getNumericValue(character);
+ }
+ throw new IllegalArgumentException("Invalid Character["
+ + leftPos
+ + "] = '"
+ + character
+ + "'");
+ }
+
+ /**
+ * Add together the individual digits in a number.
+ *
+ * @param number The number whose digits are to be added
+ * @return The sum of the digits
+ */
+ protected static int sumDigits(int number) {
+ int total = 0;
+ int todo = number;
+ while (todo > 0) {
+ total += todo % 10;
+ todo = todo / 10;
+ }
+ return total;
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
new file mode 100644
index 0000000..2c90cb0
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = SedolValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Sedol {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.Sedol.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/SedolValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/SedolValidator.java
new file mode 100644
index 0000000..4476084
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/SedolValidator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+/**
+ * Modulus 10 <b>SEDOL</b> (UK Securities) Check Digit calculation/validation.
+ * <p>
+ * SEDOL Numbers are 7 character alphanumeric codes used
+ * to identify UK Securities (SEDOL stands for Stock Exchange Daily Official List).
+ * <p>
+ * Check digit calculation is based on <i>modulus 10</i> with digits being weighted
+ * based on their position, from left to right, as follows:
+ * <p>
+ * <pre><code>
+ * position: 1 2 3 4 5 6 7
+ * weighting: 1 3 1 7 3 9 1
+ * </code></pre>
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/SEDOL">Wikipedia - SEDOL</a>
+ * for more details.
+ */
+public final class SedolValidator
+ extends ModulusValidator<Sedol> {
+
+ /** weighting given to digits depending on their right position */
+ private static final int[] POSITION_WEIGHT = new int[] {1, 3, 1, 7, 3, 9, 1};
+
+ public SedolValidator() {
+ super(10);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected int weightedValue( int charValue, int leftPos, int rightPos )
+ throws Exception {
+ return (charValue * POSITION_WEIGHT[leftPos - 1]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected int toInt( char character, int leftPos, int rightPos ) {
+ int charValue = getNumericValue(character);
+ if (charValue < 0 || charValue > 35) {
+ throw new IllegalArgumentException("Invalid Character["
+ + leftPos
+ + "] = '"
+ + charValue
+ + "'");
+ }
+ return charValue;
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
new file mode 100644
index 0000000..abdf759
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = VerhoeffValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Verhoeff {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.checkdigit.Verhoeff.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
new file mode 100644
index 0000000..19a9594
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <b>Verhoeff</b> (Dihedral) Check Digit calculation/validation.
+ * <p>
+ * Check digit calculation for numeric codes using a
+ * <a href="http://en.wikipedia.org/wiki/Dihedral_group">Dihedral Group</a>
+ * of order 10.
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/Verhoeff_algorithm">Wikipedia
+ * - Verhoeff algorithm</a> for more details.
+ */
+public final class VerhoeffValidator
+ implements ConstraintValidator<Verhoeff, String> {
+
+ /** D - multiplication table */
+ private static final int[][] D_TABLE = new int[][] {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ {1, 2, 3, 4, 0, 6, 7, 8, 9, 5},
+ {2, 3, 4, 0, 1, 7, 8, 9, 5, 6},
+ {3, 4, 0, 1, 2, 8, 9, 5, 6, 7},
+ {4, 0, 1, 2, 3, 9, 5, 6, 7, 8},
+ {5, 9, 8, 7, 6, 0, 4, 3, 2, 1},
+ {6, 5, 9, 8, 7, 1, 0, 4, 3, 2},
+ {7, 6, 5, 9, 8, 2, 1, 0, 4, 3},
+ {8, 7, 6, 5, 9, 3, 2, 1, 0, 4},
+ {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}};
+
+ /** P - permutation table */
+ private static final int[][] P_TABLE = new int[][] {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ {1, 5, 7, 6, 2, 8, 3, 0, 9, 4},
+ {5, 8, 0, 3, 7, 9, 6, 1, 4, 2},
+ {8, 9, 1, 6, 0, 4, 3, 5, 2, 7},
+ {9, 4, 5, 3, 1, 2, 6, 8, 7, 0},
+ {4, 2, 8, 6, 5, 7, 3, 9, 0, 1},
+ {2, 7, 9, 3, 8, 0, 6, 4, 1, 5},
+ {7, 0, 4, 6, 9, 1, 3, 2, 5, 8}};
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(String code, ConstraintValidatorContext context) {
+ if (code.length() == 0) {
+ return false;
+ }
+
+ int checksum = 0;
+ for (int i = 0; i < code.length(); i++) {
+ int idx = code.length() - (i + 1);
+ int num = getNumericValue(code.charAt(idx));
+ if (num < 0 || num > 9) {
+ return false;
+ }
+ checksum = D_TABLE[checksum][P_TABLE[i % 8][num]];
+ }
+ return checksum == 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(Verhoeff iban) {
+ // not needed
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/package-info.java
new file mode 100644
index 0000000..a81b2ed
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This package contains <i>Check Digit</i> validation routines.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
new file mode 100644
index 0000000..4d340d3
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(3[47]\\d{13})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface AmericanExpress {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.creditcard.AmericanExpress.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
new file mode 100644
index 0000000..3f21de8
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(30[0-5]\\d{11}|3095\\d{10}|36\\d{12}|3[8-9]\\d{12})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Diners {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.creditcard.Diners.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
new file mode 100644
index 0000000..56c3ca9
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^((6011\\d{12})|(64[4-9]\\d{13})|(65\\d{14}))$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Discover {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.creditcard.Discover.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
new file mode 100644
index 0000000..cb3a627
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(5[1-5]\\d{14})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Mastercard {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.creditcard.Mastercard.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Visa.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Visa.java
new file mode 100644
index 0000000..1ae223e
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Visa.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(4)(\\d{12}|\\d{15})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Visa {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.creditcard.Visa.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/package-info.java
new file mode 100644
index 0000000..4d10d9e
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * This package contains <i>Credit Card</i> validation routines.
+ */
+package org.apache.bval.extras.constraints.creditcard;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Directory.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Directory.java
new file mode 100644
index 0000000..92abcb2
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Directory.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.file;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = DirectoryValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Directory {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.file.Directory.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
new file mode 100644
index 0000000..61eb8ed
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.file;
+
+import java.io.File;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class DirectoryValidator implements ConstraintValidator<Directory, File> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(File value, ConstraintValidatorContext context) {
+ return value.exists() && value.isDirectory();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(Directory parameters) {
+ // do nothing (as long as Directory has no properties)
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectory.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectory.java
new file mode 100644
index 0000000..0f7deff
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectory.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.file;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is not a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = DirectoryValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface NotDirectory {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.file.NotDirectory.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectoryValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectoryValidator.java
new file mode 100644
index 0000000..0b4ff97
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectoryValidator.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.file;
+
+import java.io.File;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class NotDirectoryValidator implements ConstraintValidator<NotDirectory, File> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(File value, ConstraintValidatorContext context) {
+ return value.exists() && !value.isDirectory();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(NotDirectory parameters) {
+ // do nothing (as long as Directory has no properties)
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Symlink.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Symlink.java
new file mode 100644
index 0000000..006f8cd
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Symlink.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.file;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a symbolic link<br/>
+ */
+@Documented
+@Constraint( validatedBy = SymlinkValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Symlink {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.file.Symlink.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
new file mode 100644
index 0000000..3b16b2d
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class SymlinkValidator implements ConstraintValidator<Symlink, File> {
+
+ /**
+ * The Windows separator character.
+ */
+ private static final char WINDOWS_SEPARATOR = '\\';
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(File value, ConstraintValidatorContext context) {
+ if (!value.exists()) {
+ return false;
+ }
+
+ // routine kindly borrowed from Apache Commons-IO
+
+ if (File.separatorChar == WINDOWS_SEPARATOR) {
+ return false;
+ }
+
+ try {
+ File fileInCanonicalDir = null;
+ if (value.getParent() == null) {
+ fileInCanonicalDir = value;
+ } else {
+ File canonicalDir = value.getParentFile().getCanonicalFile();
+ fileInCanonicalDir = new File(canonicalDir, value.getName());
+ }
+
+ return (!fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()));
+ } catch (IOException e) {
+ // TODO: is it true?
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(Symlink parameters) {
+ // do nothing (as long as Symlink has no properties)
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/package-info.java
new file mode 100644
index 0000000..b877ab0
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * {@link java.io.File} constraints validators.
+ */
+package org.apache.bval.extras.constraints.file;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/Domain.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/Domain.java
new file mode 100644
index 0000000..9ff3eba
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/Domain.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.net;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = DomainValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Domain {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.net.Domain.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+ boolean allowLocal() default false;
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
new file mode 100644
index 0000000..52b3ef4
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.net;
+
+import static java.util.Arrays.asList;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <p><b>Domain name</b> validation routines.</p>
+ *
+ * <p>
+ * This validator provides methods for validating Internet domain names
+ * and top-level domains.
+ * </p>
+ *
+ * <p>Domain names are evaluated according
+ * to the standards <a href="http://www.ietf.org/rfc/rfc1034.txt">RFC1034</a>,
+ * section 3, and <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC1123</a>,
+ * section 2.1. No accomodation is provided for the specialized needs of
+ * other applications; if the domain name has been URL-encoded, for example,
+ * validation will fail even though the equivalent plaintext version of the
+ * same name would have passed.
+ * </p>
+ *
+ * <p>
+ * Validation is also provided for top-level domains (TLDs) as defined and
+ * maintained by the Internet Assigned Numbers Authority (IANA):
+ * </p>
+ *
+ * <ul>
+ * <li>{@link #isValidInfrastructureTld} - validates infrastructure TLDs
+ * (<code>.arpa</code>, etc.)</li>
+ * <li>{@link #isValidGenericTld} - validates generic TLDs
+ * (<code>.com, .org</code>, etc.)</li>
+ * <li>{@link #isValidCountryCodeTld} - validates country code TLDs
+ * (<code>.us, .uk, .cn</code>, etc.)</li>
+ * </ul>
+ *
+ * <p>
+ * (<b>NOTE</b>: This class does not provide IP address lookup for domain names or
+ * methods to ensure that a given domain name matches a specific IP; see
+ * {@link java.net.InetAddress} for that functionality.)
+ * </p>
+ */
+public class DomainValidator implements ConstraintValidator<Domain, String> {
+
+ private boolean allowLocal;
+
+ // Regular expression strings for hostnames (derived from RFC2396 and RFC 1123)
+ private static final Pattern DOMAIN_LABEL = Pattern.compile("\\p{Alnum}(?>[\\p{Alnum}-]*\\p{Alnum})*");
+
+ private static final Pattern DOMAIN_NAME_REGEX = Pattern.compile("^(?:"
+ + DOMAIN_LABEL.pattern()
+ + "\\.)+(\\p{Alpha}{2,})$");
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(String domain, ConstraintValidatorContext context) {
+ Matcher matcher = DOMAIN_NAME_REGEX.matcher(domain);
+ if (matcher.matches()) {
+ domain = matcher.group(1);
+ return isValidTld(domain);
+ } else if (allowLocal && DOMAIN_LABEL.matcher(domain).matches()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the specified <code>String</code> matches any
+ * IANA-defined top-level domain. Leading dots are ignored if present.
+ * The search is case-sensitive.
+ *
+ * @param tld the parameter to check for TLD status
+ * @return true if the parameter is a TLD
+ */
+ boolean isValidTld(String tld) {
+ if (allowLocal && isValidLocalTld(tld)) {
+ return true;
+ }
+
+ tld = chompLeadingDot(tld).toLowerCase();
+ return isValidInfrastructureTld(tld)
+ || isValidGenericTld(tld)
+ || isValidCountryCodeTld(tld);
+ }
+
+ /**
+ * Returns true if the specified <code>String</code> matches any
+ * IANA-defined infrastructure top-level domain. Leading dots are
+ * ignored if present. The search is case-sensitive.
+ *
+ * @param iTld the parameter to check for infrastructure TLD status
+ * @return true if the parameter is an infrastructure TLD
+ */
+ static boolean isValidInfrastructureTld(String iTld) {
+ return INFRASTRUCTURE_TLDS.contains(iTld);
+ }
+
+ /**
+ * Returns true if the specified <code>String</code> matches any
+ * IANA-defined generic top-level domain. Leading dots are ignored
+ * if present. The search is case-sensitive.
+ *
+ * @param gTld the parameter to check for generic TLD status
+ * @return true if the parameter is a generic TLD
+ */
+ static boolean isValidGenericTld(String gTld) {
+ return GENERIC_TLDS.contains(gTld);
+ }
+
+ /**
+ * Returns true if the specified <code>String</code> matches any
+ * IANA-defined country code top-level domain. Leading dots are
+ * ignored if present. The search is case-sensitive.
+ *
+ * @param ccTld the parameter to check for country code TLD status
+ * @return true if the parameter is a country code TLD
+ */
+ static boolean isValidCountryCodeTld(String ccTld) {
+ return COUNTRY_CODE_TLDS.contains(ccTld);
+ }
+
+ /**
+ * Returns true if the specified <code>String</code> matches any
+ * widely used "local" domains (localhost or localdomain). Leading dots are
+ * ignored if present. The search is case-sensitive.
+ *
+ * @param iTld the parameter to check for local TLD status
+ * @return true if the parameter is an local TLD
+ */
+ static boolean isValidLocalTld(String iTld) {
+ return LOCAL_TLDS.contains(iTld);
+ }
+
+ private static String chompLeadingDot(String str) {
+ if (str.startsWith(".")) {
+ return str.substring(1);
+ }
+ return str;
+ }
+
+ // ---------------------------------------------
+ // ----- TLDs defined by IANA
+ // ----- Authoritative and comprehensive list at:
+ // ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+
+ private static final List<String> INFRASTRUCTURE_TLDS = asList(
+ "arpa", // internet infrastructure
+ "root" // diagnostic marker for non-truncated root zone
+ );
+
+ private static final List<String> GENERIC_TLDS = asList(
+ "aero", // air transport industry
+ "asia", // Pan-Asia/Asia Pacific
+ "biz", // businesses
+ "cat", // Catalan linguistic/cultural community
+ "com", // commercial enterprises
+ "coop", // cooperative associations
+ "info", // informational sites
+ "jobs", // Human Resource managers
+ "mobi", // mobile products and services
+ "museum", // museums, surprisingly enough
+ "name", // individuals' sites
+ "net", // internet support infrastructure/business
+ "org", // noncommercial organizations
+ "pro", // credentialed professionals and entities
+ "tel", // contact data for businesses and individuals
+ "travel", // entities in the travel industry
+ "gov", // United States Government
+ "edu", // accredited postsecondary US education entities
+ "mil", // United States Military
+ "int" // organizations established by international treaty
+ );
+
+ private static final List<String> COUNTRY_CODE_TLDS = asList(
+ "ac", // Ascension Island
+ "ad", // Andorra
+ "ae", // United Arab Emirates
+ "af", // Afghanistan
+ "ag", // Antigua and Barbuda
+ "ai", // Anguilla
+ "al", // Albania
+ "am", // Armenia
+ "an", // Netherlands Antilles
+ "ao", // Angola
+ "aq", // Antarctica
+ "ar", // Argentina
+ "as", // American Samoa
+ "at", // Austria
+ "au", // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands)
+ "aw", // Aruba
+ "ax", // Åland
+ "az", // Azerbaijan
+ "ba", // Bosnia and Herzegovina
+ "bb", // Barbados
+ "bd", // Bangladesh
+ "be", // Belgium
+ "bf", // Burkina Faso
+ "bg", // Bulgaria
+ "bh", // Bahrain
+ "bi", // Burundi
+ "bj", // Benin
+ "bm", // Bermuda
+ "bn", // Brunei Darussalam
+ "bo", // Bolivia
+ "br", // Brazil
+ "bs", // Bahamas
+ "bt", // Bhutan
+ "bv", // Bouvet Island
+ "bw", // Botswana
+ "by", // Belarus
+ "bz", // Belize
+ "ca", // Canada
+ "cc", // Cocos (Keeling) Islands
+ "cd", // Democratic Republic of the Congo (formerly Zaire)
+ "cf", // Central African Republic
+ "cg", // Republic of the Congo
+ "ch", // Switzerland
+ "ci", // Côte d'Ivoire
+ "ck", // Cook Islands
+ "cl", // Chile
+ "cm", // Cameroon
+ "cn", // China, mainland
+ "co", // Colombia
+ "cr", // Costa Rica
+ "cu", // Cuba
+ "cv", // Cape Verde
+ "cx", // Christmas Island
+ "cy", // Cyprus
+ "cz", // Czech Republic
+ "de", // Germany
+ "dj", // Djibouti
+ "dk", // Denmark
+ "dm", // Dominica
+ "do", // Dominican Republic
+ "dz", // Algeria
+ "ec", // Ecuador
+ "ee", // Estonia
+ "eg", // Egypt
+ "er", // Eritrea
+ "es", // Spain
+ "et", // Ethiopia
+ "eu", // European Union
+ "fi", // Finland
+ "fj", // Fiji
+ "fk", // Falkland Islands
+ "fm", // Federated States of Micronesia
+ "fo", // Faroe Islands
+ "fr", // France
+ "ga", // Gabon
+ "gb", // Great Britain (United Kingdom)
+ "gd", // Grenada
+ "ge", // Georgia
+ "gf", // French Guiana
+ "gg", // Guernsey
+ "gh", // Ghana
+ "gi", // Gibraltar
+ "gl", // Greenland
+ "gm", // The Gambia
+ "gn", // Guinea
+ "gp", // Guadeloupe
+ "gq", // Equatorial Guinea
+ "gr", // Greece
+ "gs", // South Georgia and the South Sandwich Islands
+ "gt", // Guatemala
+ "gu", // Guam
+ "gw", // Guinea-Bissau
+ "gy", // Guyana
+ "hk", // Hong Kong
+ "hm", // Heard Island and McDonald Islands
+ "hn", // Honduras
+ "hr", // Croatia (Hrvatska)
+ "ht", // Haiti
+ "hu", // Hungary
+ "id", // Indonesia
+ "ie", // Ireland (Éire)
+ "il", // Israel
+ "im", // Isle of Man
+ "in", // India
+ "io", // British Indian Ocean Territory
+ "iq", // Iraq
+ "ir", // Iran
+ "is", // Iceland
+ "it", // Italy
+ "je", // Jersey
+ "jm", // Jamaica
+ "jo", // Jordan
+ "jp", // Japan
+ "ke", // Kenya
+ "kg", // Kyrgyzstan
+ "kh", // Cambodia (Khmer)
+ "ki", // Kiribati
+ "km", // Comoros
+ "kn", // Saint Kitts and Nevis
+ "kp", // North Korea
+ "kr", // South Korea
+ "kw", // Kuwait
+ "ky", // Cayman Islands
+ "kz", // Kazakhstan
+ "la", // Laos (currently being marketed as the official domain for Los Angeles)
+ "lb", // Lebanon
+ "lc", // Saint Lucia
+ "li", // Liechtenstein
+ "lk", // Sri Lanka
+ "lr", // Liberia
+ "ls", // Lesotho
+ "lt", // Lithuania
+ "lu", // Luxembourg
+ "lv", // Latvia
+ "ly", // Libya
+ "ma", // Morocco
+ "mc", // Monaco
+ "md", // Moldova
+ "me", // Montenegro
+ "mg", // Madagascar
+ "mh", // Marshall Islands
+ "mk", // Republic of Macedonia
+ "ml", // Mali
+ "mm", // Myanmar
+ "mn", // Mongolia
+ "mo", // Macau
+ "mp", // Northern Mariana Islands
+ "mq", // Martinique
+ "mr", // Mauritania
+ "ms", // Montserrat
+ "mt", // Malta
+ "mu", // Mauritius
+ "mv", // Maldives
+ "mw", // Malawi
+ "mx", // Mexico
+ "my", // Malaysia
+ "mz", // Mozambique
+ "na", // Namibia
+ "nc", // New Caledonia
+ "ne", // Niger
+ "nf", // Norfolk Island
+ "ng", // Nigeria
+ "ni", // Nicaragua
+ "nl", // Netherlands
+ "no", // Norway
+ "np", // Nepal
+ "nr", // Nauru
+ "nu", // Niue
+ "nz", // New Zealand
+ "om", // Oman
+ "pa", // Panama
+ "pe", // Peru
+ "pf", // French Polynesia With Clipperton Island
+ "pg", // Papua New Guinea
+ "ph", // Philippines
+ "pk", // Pakistan
+ "pl", // Poland
+ "pm", // Saint-Pierre and Miquelon
+ "pn", // Pitcairn Islands
+ "pr", // Puerto Rico
+ "ps", // Palestinian territories (PA-controlled West Bank and Gaza Strip)
+ "pt", // Portugal
+ "pw", // Palau
+ "py", // Paraguay
+ "qa", // Qatar
+ "re", // Réunion
+ "ro", // Romania
+ "rs", // Serbia
+ "ru", // Russia
+ "rw", // Rwanda
+ "sa", // Saudi Arabia
+ "sb", // Solomon Islands
+ "sc", // Seychelles
+ "sd", // Sudan
+ "se", // Sweden
+ "sg", // Singapore
+ "sh", // Saint Helena
+ "si", // Slovenia
+ "sj", // Svalbard and Jan Mayen Islands Not in use (Norwegian dependencies; see .no)
+ "sk", // Slovakia
+ "sl", // Sierra Leone
+ "sm", // San Marino
+ "sn", // Senegal
+ "so", // Somalia
+ "sr", // Suriname
+ "st", // São Tomé and Príncipe
+ "su", // Soviet Union (deprecated)
+ "sv", // El Salvador
+ "sy", // Syria
+ "sz", // Swaziland
+ "tc", // Turks and Caicos Islands
+ "td", // Chad
+ "tf", // French Southern and Antarctic Lands
+ "tg", // Togo
+ "th", // Thailand
+ "tj", // Tajikistan
+ "tk", // Tokelau
+ "tl", // East Timor (deprecated old code)
+ "tm", // Turkmenistan
+ "tn", // Tunisia
+ "to", // Tonga
+ "tp", // East Timor
+ "tr", // Turkey
+ "tt", // Trinidad and Tobago
+ "tv", // Tuvalu
+ "tw", // Taiwan, Republic of China
+ "tz", // Tanzania
+ "ua", // Ukraine
+ "ug", // Uganda
+ "uk", // United Kingdom
+ "um", // United States Minor Outlying Islands
+ "us", // United States of America
+ "uy", // Uruguay
+ "uz", // Uzbekistan
+ "va", // Vatican City State
+ "vc", // Saint Vincent and the Grenadines
+ "ve", // Venezuela
+ "vg", // British Virgin Islands
+ "vi", // U.S. Virgin Islands
+ "vn", // Vietnam
+ "vu", // Vanuatu
+ "wf", // Wallis and Futuna
+ "ws", // Samoa (formerly Western Samoa)
+ "ye", // Yemen
+ "yt", // Mayotte
+ "yu", // Serbia and Montenegro (originally Yugoslavia)
+ "za", // South Africa
+ "zm", // Zambia
+ "zw" // Zimbabwe
+ );
+
+ private static final List<String> LOCAL_TLDS = asList(
+ "localhost", // RFC2606 defined
+ "localdomain" // Also widely used as localhost.localdomain
+ );
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(Domain domain) {
+ allowLocal = domain.allowLocal();
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddress.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddress.java
new file mode 100644
index 0000000..4dc3bc0
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddress.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.net;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = InetAddressValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface InetAddress {
+
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.extras.constraints.net.InetAddress.message}";
+
+ Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
new file mode 100644
index 0000000..f0896f1
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.net;
+
+import java.util.regex.Pattern;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
+ *
+ * <p>This class provides methods to validate a candidate IP address.
+ */
+public class InetAddressValidator implements ConstraintValidator<InetAddress, String> {
+
+ private static final Pattern IPV4_PATTERN = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ if (!IPV4_PATTERN.matcher(value).matches()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(InetAddress parameters) {
+ // do nothing (as long as InetAddress has no properties)
+ }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/package-info.java
new file mode 100644
index 0000000..d7e7550
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * net constraints validators.
+ */
+package org.apache.bval.extras.constraints.net;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/package-info.java
new file mode 100644
index 0000000..02f423c
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Contains constraints that are NOT part of the Bean Validation specification
+ * and might disappear as soon as a final version of the specification contains
+ * similar functionalities.
+ */
+package org.apache.bval.extras.constraints;
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
new file mode 100644
index 0000000..56d5e62
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * ABA Number Validator Test.
+ */
+public class ABANumberValidatorTest extends AbstractCheckDigitTest {
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new ABANumberValidator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "123456780",
+ "123123123",
+ "011000015",
+ "111000038",
+ "231381116",
+ "121181976"
+ };
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
new file mode 100644
index 0000000..36b5198
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.ConstraintValidator;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public abstract class AbstractCheckDigitTest {
+
+ /** Check digit routine being tested */
+ private int checkDigitLth;
+
+ /** Check digit routine being tested */
+ private ConstraintValidator<? extends Annotation, String> routine;
+
+ /** Array of valid code values */
+ private String[] valid;
+
+ /** Array of invalid code values */
+ private String[] invalid;
+
+ /** code value which sums to zero */
+ private String zeroSum;
+
+ /** Prefix for error messages */
+ private String missingMessage;
+
+ public int getCheckDigitLth() {
+ return 1;
+ }
+
+ protected abstract ConstraintValidator<? extends Annotation, String> getConstraint();
+
+ protected abstract String[] getValid();
+
+ protected String[] getInvalid() {
+ return new String[] {"12345678A"};
+ }
+
+ protected String getZeroSum() {
+ return "0000000000";
+ }
+
+ public String getMissingMessage() {
+ return "Code is missing";
+ }
+
+ @Before
+ public void setUp() {
+ checkDigitLth = getCheckDigitLth();
+ routine = getConstraint();
+ valid = getValid();
+ invalid = getInvalid();
+ zeroSum = getZeroSum();
+ missingMessage = getMissingMessage();
+ }
+
+ /**
+ * Tear Down - clears routine and valid codes.
+ */
+ @After
+ public void tearDown() {
+ valid = null;
+ routine = null;
+ }
+
+ /**
+ * Test isValid() for valid values.
+ */
+ @Test
+ public void testIsValidTrue() {
+ // test valid values
+ for (int i = 0; i < valid.length; i++) {
+ assertTrue("valid[" + i +"]: " + valid[i], routine.isValid(valid[i], null));
+ }
+ }
+
+ /**
+ * Test isValid() for invalid values.
+ */
+ @Test
+ public void testIsValidFalse() {
+ // test invalid code values
+ for (int i = 0; i < invalid.length; i++) {
+ assertFalse("invalid[" + i +"]: " + invalid[i], routine.isValid(invalid[i], null));
+ }
+
+ // test invalid check digit values
+ String[] invalidCheckDigits = createInvalidCodes(valid);
+ for (int i = 0; i < invalidCheckDigits.length; i++) {
+ assertFalse("invalid check digit[" + i +"]: " + invalidCheckDigits[i], routine.isValid(invalidCheckDigits[i], null));
+ }
+ }
+
+ /**
+ * Test missing code
+ */
+ @Test
+ public void testMissingCode() {
+ // isValid() zero length
+ assertFalse("isValid() Zero Length", routine.isValid("", null));
+ }
+
+ /**
+ * Test zero sum
+ */
+ @Test
+ public void testZeroSum() {
+ assertFalse("isValid() Zero Sum", routine.isValid(zeroSum, null));
+ }
+
+ /**
+ * Returns an array of codes with invalid check digits.
+ *
+ * @param codes Codes with valid check digits
+ * @return Codes with invalid check digits
+ */
+ protected String[] createInvalidCodes(String[] codes) {
+ List<String> list = new ArrayList<String>();
+
+ // create invalid check digit values
+ for (int i = 0; i < codes.length; i++) {
+ String code = removeCheckDigit(codes[i]);
+ String check = checkDigit(codes[i]);
+ for (int j = 0; j < 10; j++) {
+ String curr = "" + Character.forDigit(j, 10);
+ if (!curr.equals(check)) {
+ list.add(code + curr);
+ }
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ /**
+ * Returns a code with the Check Digit (i.e. last character) removed.
+ *
+ * @param code The code
+ * @return The code without the check digit
+ */
+ protected String removeCheckDigit(String code) {
+ if (code == null || code.length() <= checkDigitLth) {
+ return null;
+ }
+ return code.substring(0, code.length() - checkDigitLth);
+ }
+
+ /**
+ * Returns the check digit (i.e. last character) for a code.
+ *
+ * @param code The code
+ * @return The check digit
+ */
+ protected String checkDigit(String code) {
+ if (code == null || code.length() <= checkDigitLth) {
+ return "";
+ }
+ int start = code.length() - checkDigitLth;
+ return code.substring(start);
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
new file mode 100644
index 0000000..d522626
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * CUSIP Check Digit Test.
+ */
+public class CUSIPValidatorTest extends AbstractCheckDigitTest {
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new CUSIPValidator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "037833100",
+ "931142103",
+ "837649128",
+ "392690QT3",
+ "594918104",
+ "86770G101",
+ "Y8295N109",
+ "G8572F100"
+ };
+ }
+
+ @Override
+ protected String[] getInvalid() {
+ return new String[] {"0378#3100"};
+ }
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
new file mode 100644
index 0000000..80885fe
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * EAN-13 Check Digit Test.
+ */
+public class EAN13CheckDigitTest extends AbstractCheckDigitTest {
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new EAN13Validator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "9780072129519",
+ "9780764558313",
+ "4025515373438",
+ "0095673400332"
+ };
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
new file mode 100644
index 0000000..36ed7d2
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.ConstraintValidator;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * IVAN Check Digit Test.
+ */
+public class IBANCheckDigitTest extends AbstractCheckDigitTest {
+
+ @Override
+ public int getCheckDigitLth() {
+ return 2;
+ }
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new IBANValidator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "AD1200012030200359100100",
+ "AT611904300234573201",
+ "AT611904300234573201",
+ "BE68539007547034",
+ "BE62510007547061",
+ "CY17002001280000001200527600",
+ "CZ6508000000192000145399",
+ "DK5000400440116243",
+ "EE382200221020145685",
+ "FI2112345600000785",
+ "FR1420041010050500013M02606",
+ "DE89370400440532013000",
+ "GI75NWBK000000007099453",
+ "GR1601101250000000012300695",
+ "HU42117730161111101800000000",
+ "IS140159260076545510730339",
+ "IE29AIBK93115212345678",
+ "IT60X0542811101000000123456",
+ "LV80BANK0000435195001",
+ "LT121000011101001000",
+ "LU280019400644750000",
+ "NL91ABNA0417164300",
+ "NO9386011117947",
+ "PL27114020040000300201355387",
+ "PT50000201231234567890154",
+ "SK3112000000198742637541",
+ "SI56191000000123438",
+ "ES8023100001180000012345",
+ "SE3550000000054910000003",
+ "CH3900700115201849173",
+ "GB29NWBK60161331926819"
+ };
+ }
+
+ @Override
+ protected String[] getInvalid() {
+ return new String[] {"510007+47061BE63"};
+ }
+
+ @Override
+ protected String getZeroSum() {
+ return null;
+ }
+
+ @Override
+ public String getMissingMessage() {
+ return "Invalid Code length=0";
+ }
+
+ /**
+ * Test zero sum
+ */
+ @Override
+ @Test
+ @Ignore
+ public void testZeroSum() {
+ // ignore, don't run this test
+ }
+
+ @Override
+ protected String[] createInvalidCodes( String[] codes ) {
+ List<String> list = new ArrayList<String>();
+
+ // create invalid check digit values
+ for (int i = 0; i < codes.length; i++) {
+ String code = removeCheckDigit(codes[i]);
+ String check = checkDigit(codes[i]);
+ for (int j = 0; j < 96; j++) {
+ String curr = j > 9 ? "" + j : "0" + j;
+ if (!curr.equals(check)) {
+ list.add(code.substring(0, 2) + curr + code.substring(4));
+ }
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+
+
+ /**
+ * Returns a code with the Check Digit (i.e. last character) removed.
+ *
+ * @param code The code
+ * @return The code without the check digit
+ */
+ protected String removeCheckDigit(String code) {
+ return code.substring(0, 2) + "00" + code.substring(4);
+ }
+
+ /**
+ * Returns the check digit (i.e. last character) for a code.
+ *
+ * @param code The code
+ * @return The check digit
+ */
+ protected String checkDigit(String code) {
+ if (code == null || code.length() <= getCheckDigitLth()) {
+ return "";
+ }
+ return code.substring(2, 4);
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
new file mode 100644
index 0000000..111d866
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * ISBN-10 Check Digit Test.
+ */
+public class ISBN10CheckDigitTest extends AbstractCheckDigitTest {
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new ISBN10Validator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "1930110995",
+ "020163385X",
+ "1932394354",
+ "1590596277"
+ };
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
new file mode 100644
index 0000000..85c8234
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * Luhn Check Digit Test.
+ */
+public class LuhnCheckDigitTest extends AbstractCheckDigitTest {
+
+ private static final String VALID_VISA = "4417123456789113";
+
+ private static final String VALID_SHORT_VISA = "4222222222222";
+
+ private static final String VALID_AMEX = "378282246310005";
+
+ private static final String VALID_MASTERCARD = "5105105105105100";
+
+ private static final String VALID_DISCOVER = "6011000990139424";
+
+ private static final String VALID_DINERS = "30569309025904";
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new LuhnValidator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ VALID_VISA,
+ VALID_SHORT_VISA,
+ VALID_AMEX,
+ VALID_MASTERCARD,
+ VALID_DISCOVER,
+ VALID_DINERS
+ };
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
new file mode 100644
index 0000000..0ebde22
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * Sedol Check Digit Test.
+ */
+public class SedolCheckDigitTest extends AbstractCheckDigitTest {
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new SedolValidator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "0263494",
+ "0870612",
+ "B06LQ97",
+ "3437575",
+ "B07LF55",
+ };
+ }
+
+ @Override
+ protected String[] getInvalid() {
+ return new String[] {"123#567"};
+ }
+
+ @Override
+ protected String getZeroSum() {
+ return "0000000";
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
new file mode 100644
index 0000000..2db2372
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Verhoeff Check Digit Test.
+ */
+public class VerhoeffCheckDigitTest extends AbstractCheckDigitTest {
+
+ @Override
+ protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+ return new VerhoeffValidator();
+ }
+
+ @Override
+ protected String[] getValid() {
+ return new String[] {
+ "15",
+ "1428570",
+ "12345678902"
+ };
+ }
+
+ /**
+ * Test zero sum
+ */
+ @Override
+ @Test
+ @Ignore
+ public void testZeroSum() {
+ // ignore, don't run this test
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/DomainValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/DomainValidatorTest.java
new file mode 100644
index 0000000..fcf32e4
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/DomainValidatorTest.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.extras.constraints.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.Payload;
+
+import org.apache.bval.extras.constraints.net.Domain;
+import org.apache.bval.extras.constraints.net.DomainValidator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the DomainValidator.
+ */
+public class DomainValidatorTest {
+
+ private DomainValidator validator;
+
+ @Before
+ public void setUp() {
+ validator = new DomainValidator();
+ }
+
+ @Test
+ public void testValidDomains() {
+ assertTrue("apache.org should validate", validator.isValid("apache.org", null));
+ assertTrue("www.google.com should validate", validator.isValid("www.google.com", null));
+
+ assertTrue("test-domain.com should validate", validator.isValid("test-domain.com", null));
+ assertTrue("test---domain.com should validate", validator.isValid("test---domain.com", null));
+ assertTrue("test-d-o-m-ain.com should validate", validator.isValid("test-d-o-m-ain.com", null));
+ assertTrue("two-letter domain label should validate", validator.isValid("as.uk", null));
+
+ assertTrue("case-insensitive ApAchE.Org should validate", validator.isValid("ApAchE.Org", null));
+
+ assertTrue("single-character domain label should validate", validator.isValid("z.com", null));
+
+ assertTrue("i.have.an-example.domain.name should validate", validator.isValid("i.have.an-example.domain.name", null));
+ }
+
+ @Test
+ public void testInvalidDomains() {
+ assertFalse("bare TLD .org shouldn't validate", validator.isValid(".org", null));
+ assertFalse("domain name with spaces shouldn't validate", validator.isValid(" apache.org ", null));
+ assertFalse("domain name containing spaces shouldn't validate", validator.isValid("apa che.org", null));
+ assertFalse("domain name starting with dash shouldn't validate", validator.isValid("-testdomain.name", null));
+ assertFalse("domain name ending with dash shouldn't validate", validator.isValid("testdomain-.name", null));
+ assertFalse("domain name starting with multiple dashes shouldn't validate", validator.isValid("---c.com", null));
+ assertFalse("domain name ending with multiple dashes shouldn't validate", validator.isValid("c--.com", null));
+ assertFalse("domain name with invalid TLD shouldn't validate", validator.isValid("apache.rog", null));
+
+ assertFalse("URL shouldn't validate", validator.isValid("http://www.apache.org", null));
+ assertFalse("Empty string shouldn't validate as domain name", validator.isValid(" ", null));
+ }
+
+ @Test
+ public void testTopLevelDomains() {
+ // infrastructure TLDs
+ assertTrue(".arpa should validate as iTLD", DomainValidator.isValidInfrastructureTld("arpa"));
+ assertFalse(".com shouldn't validate as iTLD", DomainValidator.isValidInfrastructureTld("com"));
+
+ // generic TLDs
+ assertTrue(".name should validate as gTLD", DomainValidator.isValidGenericTld("name"));
+ assertFalse(".us shouldn't validate as gTLD", DomainValidator.isValidGenericTld("us"));
+
+ // country code TLDs
+ assertTrue(".uk should validate as ccTLD", DomainValidator.isValidCountryCodeTld("uk"));
+ assertFalse(".org shouldn't validate as ccTLD", DomainValidator.isValidCountryCodeTld("org"));
+
+ // case-insensitive
+ assertTrue(".COM should validate as TLD", validator.isValidTld("COM"));
+ assertTrue(".BiZ should validate as TLD", validator.isValidTld("BiZ"));
+
+ // corner cases
+ assertFalse("invalid TLD shouldn't validate", validator.isValid("nope", null));
+ assertFalse("empty string shouldn't validate as TLD", validator.isValid("", null));
+ }
+
+ @Test
+ public void testAllowLocal() {
+ DomainValidator noLocal = new DomainValidator();
+ DomainValidator allowLocal = new DomainValidator();
+ allowLocal.initialize( new Domain()
+ {
+
+ public Class<? extends Annotation> annotationType() {
+ // not needed
+ return null;
+ }
+
+ public Class<? extends Payload>[] payload() {
+ // not needed
+ return null;
+ }
+
+ public String message() {
+ // not needed
+ return null;
+ }
+
+ public Class<?>[] groups() {
+ // not needed
+ return null;
+ }
+
+ public boolean allowLocal() {
+ // enable the local
+ return true;
+ }
+ });
+
+ // Default won't allow local
+ assertFalse("localhost.localdomain should validate", noLocal.isValid("localhost.localdomain", null));
+ assertFalse("localhost should validate", noLocal.isValid("localhost", null));
+
+ // But it may be requested
+ assertTrue("localhost.localdomain should validate", allowLocal.isValid("localhost.localdomain", null));
+ assertTrue("localhost should validate", allowLocal.isValid("localhost", null));
+ assertTrue("hostname should validate", allowLocal.isValid("hostname", null));
+ assertTrue("machinename should validate", allowLocal.isValid("machinename", null));
+
+ // Check the localhost one with a few others
+ assertTrue("apache.org should validate", allowLocal.isValid("apache.org", null));
+ assertFalse("domain name with spaces shouldn't validate", allowLocal.isValid(" apache.org ", null));
+ }
+
+ @Test
+ public void testIDN() {
+ assertTrue("b\u00fccher.ch in IDN should validate", validator.isValid("www.xn--bcher-kva.ch", null));
+ }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
new file mode 100644
index 0000000..4703279
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.extras.constraints.net;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.bval.extras.constraints.net.InetAddressValidator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for InetAddressValidator.
+ *
+ * @version $Revision$
+ */
+public class InetAddressValidatorTest {
+
+ private InetAddressValidator validator;
+
+ @Before
+ public void setUp() {
+ validator = new InetAddressValidator();
+ }
+
+ /**
+ * Test IPs that point to real, well-known hosts (without actually looking them up).
+ */
+ @Test
+ public void testInetAddressesFromTheWild() {
+ assertTrue("www.apache.org IP should be valid", validator.isValid("140.211.11.130", null));
+ assertTrue("www.l.google.com IP should be valid", validator.isValid("72.14.253.103", null));
+ assertTrue("fsf.org IP should be valid", validator.isValid("199.232.41.5", null));
+ assertTrue("appscs.ign.com IP should be valid", validator.isValid("216.35.123.87", null));
+ }
+
+ /**
+ * Test valid and invalid IPs from each address class.
+ */
+ @Test
+ public void testInetAddressesByClass() {
+ assertTrue("class A IP should be valid", validator.isValid("24.25.231.12", null));
+ assertFalse("illegal class A IP should be invalid", validator.isValid("2.41.32.324", null));
+
+ assertTrue("class B IP should be valid", validator.isValid("135.14.44.12", null));
+ assertFalse("illegal class B IP should be invalid", validator.isValid("154.123.441.123", null));
+
+ assertTrue("class C IP should be valid", validator.isValid("213.25.224.32", null));
+ assertFalse("illegal class C IP should be invalid", validator.isValid("201.543.23.11", null));
+
+ assertTrue("class D IP should be valid", validator.isValid("229.35.159.6", null));
+ assertFalse("illegal class D IP should be invalid", validator.isValid("231.54.11.987", null));
+
+ assertTrue("class E IP should be valid", validator.isValid("248.85.24.92", null));
+ assertFalse("illegal class E IP should be invalid", validator.isValid("250.21.323.48", null));
+ }
+
+ /**
+ * Test reserved IPs.
+ */
+ @Test
+ public void testReservedInetAddresses() {
+ assertTrue("localhost IP should be valid", validator.isValid("127.0.0.1", null));
+ assertTrue("broadcast IP should be valid", validator.isValid("255.255.255.255", null));
+ }
+
+ /**
+ * Test obviously broken IPs.
+ */
+ @Test
+ public void testBrokenInetAddresses() {
+ assertFalse("IP with characters should be invalid", validator.isValid("124.14.32.abc", null));
+ assertFalse("IP with three groups should be invalid", validator.isValid("23.64.12", null));
+ assertFalse("IP with five groups should be invalid", validator.isValid("26.34.23.77.234", null));
+ }
+
+}
diff --git a/trunk/bval-guice/pom.xml b/trunk/bval-guice/pom.xml
new file mode 100644
index 0000000..a5bd492
--- /dev/null
+++ b/trunk/bval-guice/pom.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-guice</artifactId>
+ <name>Apache BVal :: bval-guice (optional)</name>
+ <packaging>bundle</packaging>
+
+ <description>BVal - Google Guice Integration</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>org.apache.bval.bundle</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <!--
+ default profile using geronimo-validation_1.0_spec.jar
+ active when property "ri" is not present.
+ -->
+ <profile>
+ <id>geronimo</id>
+ <activation>
+ <property>
+ <name>!ri</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-validation_1.0_spec</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <!--
+ optional profile using javax.validation/validation-api.jar
+ from RI manually active when property "-Pri" is provided.
+ -->
+ <profile>
+ <id>ri</id>
+ <activation>
+ <property>
+ <name>ri</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ConfigurationStateProvider.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ConfigurationStateProvider.java
new file mode 100644
index 0000000..09c4db8
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ConfigurationStateProvider.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.apache.bval.jsr303.ConfigurationImpl;
+
+/**
+ * The {@code javax.validation.spi.ConfigurationState} provider implementation.
+ *
+ * @version $Id$
+ */
+public final class ConfigurationStateProvider implements Provider<ConfigurationState> {
+
+ @com.google.inject.Inject(optional = true)
+ private BootstrapState bootstrapState;
+
+ @Inject
+ private ValidationProvider<?> validationProvider;
+
+ @Inject
+ private TraversableResolver traversableResolver;
+
+ @Inject
+ private MessageInterpolator messageInterpolator;
+
+ @Inject
+ private ConstraintValidatorFactory constraintValidatorFactory;
+
+ public void setBootstrapState(BootstrapState bootstrapState) {
+ this.bootstrapState = bootstrapState;
+ }
+
+ public void setValidationProvider(ValidationProvider<?> validationProvider) {
+ this.validationProvider = validationProvider;
+ }
+
+ public void setTraversableResolver(TraversableResolver traversableResolver) {
+ this.traversableResolver = traversableResolver;
+ }
+
+ public void setMessageInterpolator(MessageInterpolator messageInterpolator) {
+ this.messageInterpolator = messageInterpolator;
+ }
+
+ public void setConstraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
+ this.constraintValidatorFactory = constraintValidatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigurationState get() {
+ ConfigurationImpl configuration = new ConfigurationImpl(this.bootstrapState, this.validationProvider);
+ configuration.traversableResolver(this.traversableResolver);
+ configuration.messageInterpolator(this.messageInterpolator);
+ configuration.constraintValidatorFactory(this.constraintValidatorFactory);
+ return configuration;
+ }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/GuiceAwareConstraintValidatorFactory.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/GuiceAwareConstraintValidatorFactory.java
new file mode 100644
index 0000000..4917b74
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/GuiceAwareConstraintValidatorFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+
+import com.google.inject.Injector;
+
+/**
+ * {@code javax.validation.ConstraintValidatorFactory} implementation that
+ * relies on Google Guice.
+ *
+ * @version $Id$
+ */
+@Singleton
+final class GuiceAwareConstraintValidatorFactory implements ConstraintValidatorFactory {
+
+ @Inject
+ private Injector injector;
+
+ public void setInjector(Injector injector) {
+ this.injector = injector;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+ return this.injector.getInstance(key);
+ }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/Validate.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/Validate.java
new file mode 100644
index 0000000..d1ca823
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/Validate.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.validation.ConstraintViolationException;
+
+import com.google.inject.BindingAnnotation;
+
+/**
+ * Marker for methods which arguments have to be validated.
+ *
+ * @version $Id$
+ */
+@BindingAnnotation
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface Validate {
+
+ /**
+ * The groups have to be validated, empty by default.
+ *
+ * @return the groups have to be validated, empty by default.
+ */
+ Class<?>[] groups() default {};
+
+ /**
+ * Marks if the returned object by the intercepted method execution has to
+ * be validated, false by default.
+ *
+ * @return false by default.
+ */
+ boolean validateReturnedValue() default false;
+
+ /**
+ * The exception re-thrown when an error occurs during the validation.
+ *
+ * @return the exception re-thrown when an error occurs during the
+ * validation.
+ */
+ Class<? extends Throwable> rethrowExceptionsAs() default ConstraintViolationException.class;
+
+ /**
+ * A custom error message when throwing the custom exception.
+ *
+ * It supports java.util.Formatter place holders, intercepted method
+ * arguments will be used as message format arguments.
+ *
+ * @return a custom error message when throwing the custom exception.
+ * @see java.util.Formatter#format(String, Object...)
+ */
+ String exceptionMessage() default "";
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidateMethodInterceptor.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidateMethodInterceptor.java
new file mode 100644
index 0000000..55ff481
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidateMethodInterceptor.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.bval.jsr303.extensions.MethodValidator;
+
+/**
+ * Method interceptor for {@link Validate} annotation.
+ *
+ * @version $Id$
+ */
+public final class ValidateMethodInterceptor implements MethodInterceptor {
+
+ private static final Class<?>[] CAUSE_TYPES = new Class[]{ Throwable.class };
+
+ private static final Class<?>[] MESSAGE_CAUSE_TYPES = new Class[]{ String.class, Throwable.class };
+
+ /**
+ * The {@link ValidatorFactory} reference.
+ */
+ @Inject
+ private ValidatorFactory validatorFactory;
+
+ /**
+ * Sets the {@link ValidatorFactory} reference.
+ *
+ * @param validatorFactory the {@link ValidatorFactory} reference
+ */
+ public void setValidatorFactory(ValidatorFactory validatorFactory) {
+ this.validatorFactory = validatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ Validate validate = invocation.getMethod().getAnnotation(Validate.class);
+
+ Validator validator = this.validatorFactory.getValidator();
+ MethodValidator methodValidator = validator.unwrap(MethodValidator.class);
+
+ Set<ConstraintViolation<?>> constraintViolations = new HashSet<ConstraintViolation<?>>();
+ Class<?> clazz = invocation.getMethod().getDeclaringClass();
+ Method method = invocation.getMethod();
+ Object[] arguments = invocation.getArguments();
+ Class<?>[] groups = validate.groups();
+
+ constraintViolations.addAll(methodValidator.validateParameters(clazz,
+ method,
+ arguments,
+ groups));
+
+ if (!constraintViolations.isEmpty()) {
+ throw getException(new ConstraintViolationException(
+ String.format("Validation error when calling method '%s' with arguments %s",
+ method,
+ Arrays.deepToString(arguments)),
+ constraintViolations),
+ validate.rethrowExceptionsAs(),
+ validate.exceptionMessage(),
+ arguments);
+ }
+
+ Object returnedValue = invocation.proceed();
+
+ if (validate.validateReturnedValue()) {
+ constraintViolations.addAll(methodValidator.validateReturnedValue(clazz, method, returnedValue, groups));
+
+ if (!constraintViolations.isEmpty()) {
+ throw getException(new ConstraintViolationException(
+ String.format("Method '%s' returned a not valid value %s",
+ method,
+ returnedValue),
+ constraintViolations),
+ validate.rethrowExceptionsAs(),
+ validate.exceptionMessage(),
+ arguments);
+ }
+ }
+
+ return returnedValue;
+ }
+
+ /**
+ * Define the {@link Throwable} has to be thrown when a validation error
+ * occurs and the user defined the custom error wrapper.
+ *
+ * @param exception the occurred validation error.
+ * @param exceptionWrapperClass the user defined custom error wrapper.
+ * @return the {@link Throwable} has o be thrown.
+ */
+ private static Throwable getException(ConstraintViolationException exception,
+ Class<? extends Throwable> exceptionWrapperClass,
+ String exceptionMessage,
+ Object[] arguments) {
+ // check the thrown exception is of same re-throw type
+ if (exceptionWrapperClass == ConstraintViolationException.class) {
+ return exception;
+ }
+
+ // re-throw the exception as new exception
+ Throwable rethrowEx = null;
+
+ String errorMessage;
+ Object[] initargs;
+ Class<?>[] initargsType;
+
+ if (exceptionMessage.length() != 0) {
+ errorMessage = String.format(exceptionMessage, arguments);
+ initargs = new Object[]{ errorMessage, exception };
+ initargsType = MESSAGE_CAUSE_TYPES;
+ } else {
+ initargs = new Object[]{ exception };
+ initargsType = CAUSE_TYPES;
+ }
+
+ Constructor<? extends Throwable> exceptionConstructor = getMatchingConstructor(exceptionWrapperClass, initargsType);
+ if (exceptionConstructor != null) {
+ try {
+ rethrowEx = exceptionConstructor.newInstance(initargs);
+ } catch (Exception e) {
+ errorMessage = String.format("Impossible to re-throw '%s', it needs the constructor with %s argument(s).",
+ exceptionWrapperClass.getName(),
+ Arrays.toString(initargsType));
+ rethrowEx = new RuntimeException(errorMessage, e);
+ }
+ } else {
+ errorMessage = String.format("Impossible to re-throw '%s', it needs the constructor with %s or %s argument(s).",
+ exceptionWrapperClass.getName(),
+ Arrays.toString(CAUSE_TYPES),
+ Arrays.toString(MESSAGE_CAUSE_TYPES));
+ rethrowEx = new RuntimeException(errorMessage);
+ }
+
+ return rethrowEx;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <E extends Throwable> Constructor<E> getMatchingConstructor(Class<E> type,
+ Class<?>[] argumentsType) {
+ Class<? super E> currentType = type;
+ while (Object.class != currentType) {
+ for (Constructor<?> constructor : currentType.getConstructors()) {
+ if (Arrays.equals(argumentsType, constructor.getParameterTypes())) {
+ return (Constructor<E>) constructor;
+ }
+ }
+ currentType = currentType.getSuperclass();
+ }
+ return null;
+ }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidationModule.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidationModule.java
new file mode 100644
index 0000000..6ddbd6e
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidationModule.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+import org.apache.bval.jsr303.resolver.DefaultTraversableResolver;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.matcher.Matchers;
+
+/**
+ * The Google-Guice module for Apache BVal.
+ *
+ * @version $Id$
+ */
+public final class ValidationModule extends AbstractModule {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void configure() {
+ // apache bval bootstrap
+ this.bind(MessageInterpolator.class).to(DefaultMessageInterpolator.class).in(Scopes.SINGLETON);
+ this.bind(TraversableResolver.class).to(DefaultTraversableResolver.class).in(Scopes.SINGLETON);
+ this.bind(ConstraintValidatorFactory.class).to(GuiceAwareConstraintValidatorFactory.class);
+ this.bind(new TypeLiteral<ValidationProvider<?>>() {}).to(ApacheValidationProvider.class).in(Scopes.SINGLETON);
+ this.bind(ConfigurationState.class).toProvider(ConfigurationStateProvider.class).in(Scopes.SINGLETON);
+ this.bind(ValidatorFactory.class).toProvider(ValidatorFactoryProvider.class).in(Scopes.SINGLETON);
+ this.bind(Validator.class).toProvider(ValidatorProvider.class);
+
+ // AOP stuff
+ MethodInterceptor validateMethodInterceptor = new ValidateMethodInterceptor();
+ this.binder().requestInjection(validateMethodInterceptor);
+ this.bindInterceptor(Matchers.any(), Matchers.annotatedWith(Validate.class), validateMethodInterceptor);
+ }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorFactoryProvider.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorFactoryProvider.java
new file mode 100644
index 0000000..7d9d477
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorFactoryProvider.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+import org.apache.bval.jsr303.ApacheValidationProvider;
+
+/**
+ * Validator Factory guice provider implementation.
+ *
+ * @version $Id$
+ */
+@Singleton
+final class ValidatorFactoryProvider implements Provider<ValidatorFactory> {
+
+ @Inject
+ private ConfigurationState configurationState;
+
+ public void setConfigurationState(ConfigurationState configurationState) {
+ this.configurationState = configurationState;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ValidatorFactory get() {
+ return new ApacheValidationProvider().buildValidatorFactory(this.configurationState);
+ }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorProvider.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorProvider.java
new file mode 100644
index 0000000..06262dd
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+/**
+ * Validator guice provider implementation.
+ *
+ * @version $Id$
+ */
+@Singleton
+final class ValidatorProvider implements Provider<Validator> {
+
+ /**
+ * The validator reference.
+ */
+ @Inject
+ private ValidatorFactory validatorFactory;
+
+ public void setValidatorFactory(ValidatorFactory validatorFactory) {
+ this.validatorFactory = validatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Validator get() {
+ return this.validatorFactory.getValidator();
+ }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/package-info.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/package-info.java
new file mode 100644
index 0000000..b48dc05
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Contains Apache BVal support for Google-Guice.
+ *
+ * @version $Id$
+ */
+package org.apache.bval.guice;
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/Country.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Country.java
new file mode 100644
index 0000000..d35b182
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Country.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ *
+ *
+ * @version $Id$
+ */
+public class Country {
+
+ @NotNull(groups = { Update.class })
+ private Long id;
+
+ @NotNull(groups = { Insert.class })
+ private String name;
+
+ @NotNull(groups = { Insert.class })
+ @Size(max = 2, groups = { Insert.class, Update.class })
+ private String iso2Code;
+
+ @NotNull(groups = { Insert.class })
+ @Size(max = 3, groups = { Insert.class, Update.class })
+ private String iso3Code;
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getIso2Code() {
+ return iso2Code;
+ }
+
+ public void setIso2Code(String iso2Code) {
+ this.iso2Code = iso2Code;
+ }
+
+ public String getIso3Code() {
+ return iso3Code;
+ }
+
+ public void setIso3Code(String iso3Code) {
+ this.iso3Code = iso3Code;
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyCountryDao.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyCountryDao.java
new file mode 100644
index 0000000..acc317e
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyCountryDao.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import javax.inject.Singleton;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ *
+ *
+ * @version $Id$
+ */
+@Singleton
+public class DummyCountryDao {
+
+ @Validate(
+ groups = { Insert.class },
+ validateReturnedValue = true
+ )
+ public Country insertCountry(@NotNull(groups = { Insert.class }) String name,
+ @NotNull(groups = { Insert.class }) @Size(max = 2, groups = { Insert.class, Update.class }) String iso2Code,
+ @NotNull(groups = { Insert.class }) @Size(max = 3, groups = { Insert.class, Update.class }) String iso3Code) {
+ Country country = new Country();
+ country.setName(name);
+ country.setIso2Code(iso2Code);
+ country.setIso3Code(iso3Code);
+ return country;
+ }
+
+ @Validate(
+ groups = { Update.class },
+ rethrowExceptionsAs = DummyException.class,
+ exceptionMessage = "This is just a dummy message %s"
+ )
+ public int updateCountry(@Valid Country country) {
+ return 0;
+ }
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyException.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyException.java
new file mode 100644
index 0000000..764ca71
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyException.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+/**
+ *
+ *
+ * @version $Id$
+ */
+public final class DummyException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public DummyException(Throwable cause) {
+ super(cause);
+ }
+
+ public DummyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/GuiceAwareValidationTestCase.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/GuiceAwareValidationTestCase.java
new file mode 100644
index 0000000..427bb92
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/GuiceAwareValidationTestCase.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.guice;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+
+import junit.framework.TestCase;
+
+import com.google.inject.Guice;
+
+/**
+ *
+ *
+ * @version $Id$
+ */
+public final class GuiceAwareValidationTestCase extends TestCase {
+
+ @Inject
+ private Validator validator;
+
+ @Inject
+ private DummyCountryDao dummyCountryDao;
+
+ public void setValidator(Validator validator) {
+ this.validator = validator;
+ }
+
+ public void setDummyCountryDao(DummyCountryDao dummyCountryDao) {
+ this.dummyCountryDao = dummyCountryDao;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ Guice.createInjector(new ValidationModule()).injectMembers(this);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ this.validator = null;
+ this.dummyCountryDao = null;
+ }
+
+ public void testInjectedValidation() {
+ Country country = new Country();
+ country.setName("Italy");
+ country.setIso2Code("it");
+ country.setIso3Code("ita");
+
+ Set<ConstraintViolation<Country>> violations = this.validator.validate(country);
+ assertTrue(violations.isEmpty());
+ }
+
+ public void testAOPInjectedValidation() {
+ this.dummyCountryDao.insertCountry("Italy", "it", "ita");
+ }
+
+ public void testAOPInjectedFailedValidation() {
+ try {
+ this.dummyCountryDao.insertCountry("Italy", "ita", "ita");
+ fail("javax.validation.ConstraintViolationException expected");
+ } catch (ConstraintViolationException cve) {
+ // do nothing
+ }
+ }
+
+ public void testRethrowWrappedException() {
+ try {
+ this.dummyCountryDao.updateCountry(new Country());
+ fail("org.apache.bval.guice.DummyException expected");
+ } catch (Exception e) {
+ assertEquals(DummyException.class, e.getClass());
+ assertTrue(e.getMessage().startsWith("This is just a dummy message "));
+ }
+ }
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/Insert.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Insert.java
new file mode 100644
index 0000000..9384691
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Insert.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.guice;
+
+/**
+ *
+ *
+ * @version $Id$
+ */
+public interface Insert {
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/Update.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Update.java
new file mode 100644
index 0000000..e3fdecb
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Update.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.guice;
+
+/**
+ *
+ *
+ * @version $Id$
+ */
+public interface Update {
+
+}
diff --git a/trunk/bval-json/pom.xml b/trunk/bval-json/pom.xml
new file mode 100644
index 0000000..492bc89
--- /dev/null
+++ b/trunk/bval-json/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-json</artifactId>
+ <name>Apache BVal :: bval-json (optional)</name>
+ <packaging>bundle</packaging>
+
+ <description>BVal - Optional JSON Component</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-xstream</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-core</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-xstream</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+
+ </build>
+</project>
+
diff --git a/trunk/bval-json/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bval-json/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..9a0c1ee
--- /dev/null
+++ b/trunk/bval-json/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,25 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-json/src/main/java/org/apache/bval/json/JSONGenerator.java b/trunk/bval-json/src/main/java/org/apache/bval/json/JSONGenerator.java
new file mode 100644
index 0000000..dae9099
--- /dev/null
+++ b/trunk/bval-json/src/main/java/org/apache/bval/json/JSONGenerator.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.json;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.bval.model.MetaBean;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+/**
+ * Description: Generate a JSON String for a collection of {@link MetaBean}s.
+ * This implementation uses a freemarker template to generate the output.<br/>
+ *
+ * This is an optional module which requires the freemarker dependency
+ * and template resourece file "bean-infos-json.ftl" with it.
+ */
+public class JSONGenerator {
+ private final Template template;
+
+ public JSONGenerator() throws IOException {
+ this("bean-infos-json.ftl");
+ }
+
+ public JSONGenerator(String templateName) throws IOException {
+ Configuration freemarker = new Configuration();
+ freemarker.setNumberFormat("0.######"); // prevent locale-sensitive number format
+ freemarker.setClassForTemplateLoading(getClass(), "");
+ template = freemarker.getTemplate(templateName);
+ }
+
+ public JSONGenerator(Template template) {
+ this.template = template;
+ }
+
+ public String toJSON(MetaBean metaBean) throws IOException, TemplateException {
+ List<MetaBean> metaBeans = new ArrayList<MetaBean>(1);
+ metaBeans.add(metaBean);
+ return toJSON(metaBeans);
+ }
+
+ public String toJSON(Collection<MetaBean> metaBeans)
+ throws IOException, TemplateException {
+ final StringWriter out = new StringWriter();
+ toJSON(metaBeans, out);
+ return out.toString();
+ }
+
+ public void toJSON(Collection<MetaBean> metaBeans, Writer out)
+ throws IOException, TemplateException {
+ Map<String, Object> rootMap = new HashMap<String, Object>();
+ rootMap.put("metaBeans", metaBeans);
+ rootMap.put("generator", this);
+ template.process(rootMap, out);
+ }
+}
diff --git a/trunk/bval-json/src/main/resources/org/apache/bval/json/bean-infos-json.ftl b/trunk/bval-json/src/main/resources/org/apache/bval/json/bean-infos-json.ftl
new file mode 100644
index 0000000..63012ad
--- /dev/null
+++ b/trunk/bval-json/src/main/resources/org/apache/bval/json/bean-infos-json.ftl
@@ -0,0 +1,85 @@
+bval.namespace("bval.metadata");
+
+<#--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+
+(function(){
+<#assign var = 0>
+<#assign varrefs = {}>
+
+<#list metaBeans as metaBean>
+<#assign varrefs = varrefs + {metaBean.id : var}>
+var metaBean${var} = {
+ "id" : "${metaBean.id}",
+ <#if metaBean.beanClass??>"beanClass" : "${metaBean.beanClass.name}",</#if>
+ <#if metaBean.name??>"name" : "${metaBean.name}",</#if>
+ "features" :{<#rt/>
+ <#list metaBean.features?keys as featureKey>
+ <#assign value = metaBean.features[featureKey]>
+ "${featureKey}" : <#if
+ value?is_string>"${value}"<#elseif
+ value?is_boolean>${value?string}<#elseif
+ value?is_number>${value}<#elseif
+ value?is_date>${value?string.full}<#elseif
+ !(value??)>null<#elseif value?is_sequence>[<#list value as v>"${v}"<#if v_has_next>,</#if></#list>]<#elseif
+ value?is_hash>{<#list value?keys as key>"${key}" : "${value[key]}"<#if key_has_next>,</#if></#list>}<#else
+ >"?"</#if><#rt/><#if
+ featureKey_has_next>,</#if>
+ </#list>
+ <#lt/>},
+ "properties" :{
+ <#list metaBean.properties as property>
+ "${property.name}":{
+ "name" : "${property.name}",
+ <#if property.type??>"type" : "${property.type}",</#if>
+ <#if property.typeClass??>"typeClass" : "${property.typeClass.name}",</#if>
+ "features" : {<#if property.type?? &&
+ property.type.enum!false>"enum" : {<#list property.type.enumConstants as enum>"${enum.name()}": "${enum.name()}"<#if enum_has_next>, </#if></#list>}<#if property.features?size > 0>,</#if></#if><#list
+ property.features?keys as featureKey>
+ <#assign value = property.features[featureKey]>
+ "${featureKey}" : <#rt/><#if
+ value?is_string>"${value}"<#elseif
+ value?is_boolean>${value?string}<#elseif
+ value?is_number>${value}<#elseif
+ value?is_date>${value?string.full}<#elseif
+ !(value??)>null<#elseif value?is_sequence>[<#list value as v>"${v}"<#if v_has_next>,</#if></#list>]<#elseif
+ value?is_hash_ex>{<#list value?keys as key>"${key}" : "${value[key]}"<#if key_has_next>,</#if></#list>}<#else
+ >"?"</#if><#rt/><#if featureKey_has_next>,</#if>
+ </#list>
+ }
+ }<#if property_has_next>,</#if>
+ </#list>
+ }
+};
+<#assign var = var + 1>
+</#list>
+
+<#assign var = 0>
+<#list metaBeans as metaBean><#list
+ metaBean.properties as property><#if
+ property.metaBean?? && varrefs[property.metaBean.id]??>
+metaBean${var}.properties.${property.name}.metaBean = metaBean${varrefs[property.metaBean.id]};
+</#if></#list><#assign var = var + 1></#list><#assign var = 0>
+bval.metadata.metaBeans = {
+<#list metaBeans as metaBean>
+ "${metaBean.id}" : metaBean${var}<#if metaBean_has_next>,</#if>
+ <#assign var = var + 1>
+</#list>};
+})();
diff --git a/trunk/bval-json/src/test/java/org/apache/bval/json/JSONGeneratorTest.java b/trunk/bval-json/src/test/java/org/apache/bval/json/JSONGeneratorTest.java
new file mode 100644
index 0000000..c69cf39
--- /dev/null
+++ b/trunk/bval-json/src/test/java/org/apache/bval/json/JSONGeneratorTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.json;
+
+import junit.framework.TestCase;
+import org.apache.bval.example.BusinessEnum;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.example.BusinessObjectAddress;
+import org.apache.bval.model.DynaTypeEnum;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.xml.XMLMetaBeanManager;
+import org.apache.bval.xml.XMLMetaBeanURLLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: <br>
+ * Author: roman.stumm<br>
+ */
+public class JSONGeneratorTest extends TestCase {
+ private XMLMetaBeanManager mbm;
+
+ public JSONGeneratorTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mbm = new XMLMetaBeanManager();
+ mbm.addLoader(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos.xml")));
+ }
+
+ public void testBeanInfosCustomPatchGenerated() throws Exception {
+ MetaBean mbean = mbm.findForClass(BusinessObject.class);
+ MetaProperty mprop = mbean.getProperty("lastName");
+ assertTrue(mprop.isMandatory());
+
+ mbm.getCache().removeFromCache(mbean);
+ mbm.addLoader(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos-custom.xml")));
+ mbean = mbm.findForClass(BusinessObject.class);
+ mprop = mbean.getProperty("lastName");
+ assertTrue(!mprop.isMandatory());
+
+ JSONGenerator converter = new JSONGenerator();
+
+ List<MetaBean> metaBeans = new ArrayList<MetaBean>(2);
+ metaBeans.add(mbean);
+ MetaBean mbean2 = mbm.findForId("UnknownObject");
+ metaBeans.add(mbean2);
+ String json = converter.toJSON(metaBeans);
+ assertNotNull(json);
+ // System.out.println(json);
+ }
+
+ public void testJSON() throws Exception {
+ MetaBean info = mbm.findForClass(BusinessObject.class);
+ MetaBean info2 = info.getProperty("address").getMetaBean();
+
+ // empty default bean without xml backup
+ MetaBean info3 = mbm.findForClass(BusinessObjectAddress.class);
+ JSONGenerator converter = new JSONGenerator();
+
+ List<MetaBean> metaBeans = new ArrayList<MetaBean>(2);
+ metaBeans.add(info);
+ metaBeans.add(info2);
+ metaBeans.add(info3);
+ String json = converter.toJSON(metaBeans);
+ assertNotNull(json);
+ // System.out.println(json);
+ }
+
+ public void testJSON_dynaTypeEnum() throws Exception {
+ MetaBean info = mbm.findForClass(BusinessObject.class);
+ MetaProperty choice = info.getProperty("choice");
+ choice.setType(new DynaTypeEnum(BusinessEnum.class, "CUSTOM_1", "CUSTOM_2"));
+
+ JSONGenerator converter = new JSONGenerator();
+
+ List<MetaBean> metaBeans = new ArrayList<MetaBean>(1);
+ metaBeans.add(info);
+ String json = converter.toJSON(metaBeans);
+ assertNotNull(json);
+ // System.out.println(json);
+ assertTrue(json.indexOf("CUSTOM_1") > 0);
+ assertTrue(json.indexOf("CUSTOM_2") > 0);
+ assertTrue(json.indexOf("VALUE1") < 0);
+ }
+}
diff --git a/trunk/bval-jsr303/pom.xml b/trunk/bval-jsr303/pom.xml
new file mode 100644
index 0000000..e20d121
--- /dev/null
+++ b/trunk/bval-jsr303/pom.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to you under the Apache License, Version
+ 2.0 (the "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+ applicable law or agreed to in writing, software distributed under the
+ License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied. See the License for
+ the specific language governing permissions and limitations under the
+ License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-jsr303</artifactId>
+ <name>Apache BVal :: bval-jsr303</name>
+ <packaging>jar</packaging>
+
+ <description>Implementation specific classes for JSR 303 Bean Validation 1.0</description>
+
+ <profiles>
+ <profile>
+ <id>jaxb</id>
+ <activation>
+ <jdk>1.5</jdk>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>javax.xml.bind</groupId>
+ <artifactId>jaxb-api</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-impl</artifactId>
+ <version>2.1.3</version>
+ </dependency>
+ </dependencies>
+ </profile>
+ <!--
+ default profile using geronimo-validation_1.0_spec.jar active when
+ property "ri" is not present.
+ -->
+ <profile>
+ <id>geronimo</id>
+ <activation>
+ <property>
+ <name>!ri</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-validation_1.0_spec</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <!--
+ optional profile using javax.validation/validation-api.jar from RI
+ manually active when property "-Dri" is provided.
+ -->
+ <profile>
+ <id>ri</id>
+ <activation>
+ <property>
+ <name>ri</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+ </profile>
+ <profile>
+ <id>sec</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>**/*Test.java</include>
+ <include>**/*TestCase.java</include>
+ </includes>
+ <argLine>-Djava.security.manager -Djava.security.policy=${project.basedir}/src/test/resources/java.policy</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- optional dependencies -->
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-xstream</artifactId>
+ <version>${project.version}</version>
+ <!-- don't pull into OSGi bundle -->
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jpa_2.0_spec</artifactId>
+ <!-- allow users to choose an API provider -->
+ <scope>provided</scope>
+ <optional>true</optional>
+ </dependency>
+ <!-- Testing dependencies -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ <resource>
+ <directory>src/main/xsd</directory>
+ <targetPath>META-INF</targetPath>
+ </resource>
+ </resources>
+
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jaxb2-maven-plugin</artifactId>
+ <version>1.2</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>xjc</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <packageName>org.apache.bval.jsr303.xml</packageName>
+ <extension>true</extension>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>test-jar</id>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ <inherited>false</inherited>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <!-- 2.12 has http://jira.codehaus.org/browse/SUREFIRE-836 -->
+ <version>2.11</version>
+ <configuration>
+ <includes>
+ <include>**/*Test.java</include>
+ <include>**/*TestCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/bval-jsr303/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bval-jsr303/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..05ddae6
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,26 @@
+##
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements. See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership. The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License. You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied. See the License for the
+## specific language governing permissions and limitations
+## under the License.
+##
+
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
new file mode 100644
index 0000000..6303452
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.AssertFalse;
+
+/**
+ * Description: assert that value is false<br/>
+ */
+public class AssertFalseValidator implements ConstraintValidator<AssertFalse, Boolean> {
+
+ public void initialize(AssertFalse annotation) {
+ }
+
+ public boolean isValid(Boolean value, ConstraintValidatorContext context) {
+ return value == null || !value;
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
new file mode 100644
index 0000000..6b7be14
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.AssertTrue;
+
+/**
+ * Description: assert that value is true<br/>
+ */
+public class AssertTrueValidator implements ConstraintValidator<AssertTrue, Boolean> {
+
+ public void initialize(AssertTrue annotation) {
+ }
+
+ public boolean isValid(Boolean value, ConstraintValidatorContext context) {
+ return value == null || value;
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
new file mode 100644
index 0000000..b9222bd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMax;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/** Description: validate that number-value of passed object is <= maxvalue<br/> */
+public class DecimalMaxValidatorForNumber
+ implements ConstraintValidator<DecimalMax, Number> {
+
+ private BigDecimal maxValue;
+
+ public void initialize(DecimalMax annotation) {
+ try {
+ this.maxValue = new BigDecimal(annotation.value());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ annotation.value() + " does not represent a valid BigDecimal format");
+ }
+ }
+
+ public boolean isValid(Number value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ } else if (value instanceof BigDecimal) {
+ return ((BigDecimal) value).compareTo(maxValue) != 1;
+ } else if (value instanceof BigInteger) {
+ return (new BigDecimal((BigInteger) value)).compareTo(maxValue) != 1;
+ } else {
+ return (new BigDecimal(value.doubleValue()).compareTo(maxValue)) != 1;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
new file mode 100644
index 0000000..5f1a863
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMax;
+import java.math.BigDecimal;
+
+/**
+ * Check that the String being validated represents a number, and has a value
+ * <= maxvalue
+ */
+public class DecimalMaxValidatorForString
+ implements ConstraintValidator<DecimalMax, String> {
+
+ private BigDecimal maxValue;
+
+ public void initialize(DecimalMax annotation) {
+ try {
+ this.maxValue = new BigDecimal(annotation.value());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ annotation.value() + " does not represent a valid BigDecimal format");
+ }
+ }
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ }
+ try {
+ return new BigDecimal(value).compareTo(maxValue) != 1;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
new file mode 100644
index 0000000..b24a80f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMin;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/** Description: validate that number-value of passed object is >= minvalue<br/> */
+public class DecimalMinValidatorForNumber
+ implements ConstraintValidator<DecimalMin, Number> {
+
+ private BigDecimal minValue;
+
+ public void initialize(DecimalMin annotation) {
+ try {
+ this.minValue = new BigDecimal(annotation.value());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ annotation.value() + " does not represent a valid BigDecimal format");
+ }
+ }
+
+ public boolean isValid(Number value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ } else if (value instanceof BigDecimal) {
+ return ((BigDecimal) value).compareTo(minValue) != -1;
+ } else if (value instanceof BigInteger) {
+ return (new BigDecimal((BigInteger) value)).compareTo(minValue) != -1;
+ } else {
+ return (new BigDecimal(value.doubleValue()).compareTo(minValue)) != -1;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
new file mode 100644
index 0000000..7859d53
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMin;
+import java.math.BigDecimal;
+
+/**
+ * Description:
+ * Check that the String being validated represents a number, and has a value
+ * >= minvalue
+ */
+public class DecimalMinValidatorForString
+ implements ConstraintValidator<DecimalMin, String> {
+
+ private BigDecimal minValue;
+
+ public void initialize(DecimalMin annotation) {
+ try {
+ this.minValue = new BigDecimal(annotation.value());
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ annotation.value() + " does not represent a valid BigDecimal format");
+ }
+ }
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ //null values are valid
+ if (value == null) {
+ return true;
+ }
+ try {
+ return new BigDecimal(value).compareTo(minValue) != -1;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForNumber.java
new file mode 100644
index 0000000..5975f22
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForNumber.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Digits;
+import java.math.BigDecimal;
+
+/**
+ * Validates that the <code>Number</code> being validates matches the pattern
+ * defined in the constraint.
+ */
+public class DigitsValidatorForNumber implements ConstraintValidator<Digits, Number> {
+
+ private int integral;
+ private int fractional;
+
+ public int getIntegral() {
+ return integral;
+ }
+
+ public void setIntegral(int integral) {
+ this.integral = integral;
+ }
+
+ public int getFractional() {
+ return fractional;
+ }
+
+ public void setFractional(int fractional) {
+ this.fractional = fractional;
+ }
+
+ public void initialize(Digits annotation) {
+ this.integral = annotation.integer();
+ this.fractional = annotation.fraction();
+ if (integral < 0) {
+ throw new IllegalArgumentException(
+ "The length of the integer part cannot be negative.");
+ }
+ if (fractional < 0) {
+ throw new IllegalArgumentException(
+ "The length of the fraction part cannot be negative.");
+ }
+ }
+
+ public boolean isValid(Number num, ConstraintValidatorContext context) {
+ if (num == null) {
+ return true;
+ }
+
+ BigDecimal bigDecimal;
+ if (num instanceof BigDecimal) {
+ bigDecimal = (BigDecimal) num;
+ } else {
+ bigDecimal = new BigDecimal(num.toString());
+ }
+ bigDecimal = bigDecimal.stripTrailingZeros();
+
+ int intLength = bigDecimal.precision() - bigDecimal.scale();
+ if (integral >= intLength) {
+ int factionLength = bigDecimal.scale() < 0 ? 0 : bigDecimal.scale();
+ return fractional >= factionLength;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForString.java
new file mode 100644
index 0000000..752f887
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForString.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Digits;
+import java.math.BigDecimal;
+
+/**
+ * Validates that the <code>String</code> being validated consists of digits,
+ * and matches the pattern defined in the constraint.
+ */
+public class DigitsValidatorForString implements ConstraintValidator<Digits, String> {
+
+ private int integral;
+ private int fractional;
+
+ public int getIntegral() {
+ return integral;
+ }
+
+ public void setIntegral(int integral) {
+ this.integral = integral;
+ }
+
+ public int getFractional() {
+ return fractional;
+ }
+
+ public void setFractional(int fractional) {
+ this.fractional = fractional;
+ }
+
+ public void initialize(Digits annotation) {
+ this.integral = annotation.integer();
+ this.fractional = annotation.fraction();
+ if (integral < 0) {
+ throw new IllegalArgumentException(
+ "The length of the integer part cannot be negative.");
+ }
+ if (fractional < 0) {
+ throw new IllegalArgumentException(
+ "The length of the fraction part cannot be negative.");
+ }
+ }
+
+ public boolean isValid(String str, ConstraintValidatorContext context) {
+ //null values are valid
+ if (str == null) {
+ return true;
+ }
+
+ BigDecimal bigDecimal = getBigDecimalValue(str);
+ if (bigDecimal == null) {
+ return false;
+ }
+
+ int intLength = bigDecimal.precision() - bigDecimal.scale();
+ if (integral >= intLength) {
+ int factionLength = bigDecimal.scale() < 0 ? 0 : bigDecimal.scale();
+ return fractional >= factionLength;
+ } else {
+ return false;
+ }
+ }
+
+ private BigDecimal getBigDecimalValue(String str) {
+ BigDecimal bd;
+ try {
+ bd = new BigDecimal(str);
+ } catch (NumberFormatException nfe) {
+ return null;
+ }
+ return bd;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/Email.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/Email.java
new file mode 100644
index 0000000..dc97236
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/Email.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate an email address (by pattern)<br/>
+ */
+@Documented
+@Constraint(validatedBy = EmailValidator.class)
+@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER})
+@Retention(RUNTIME)
+public @interface Email {
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.constraints.Email.message}";
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/EmailValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/EmailValidator.java
new file mode 100644
index 0000000..39dd845
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/EmailValidator.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import org.apache.bval.routines.EMailValidationUtils;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class EmailValidator implements ConstraintValidator<Email, CharSequence> {
+
+ public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
+ return EMailValidationUtils.isValid(value);
+ }
+
+ public void initialize(Email parameters) {
+ // do nothing (as long as Email has no properties)
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForCalendar.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForCalendar.java
new file mode 100644
index 0000000..920ecd8
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForCalendar.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Future;
+import java.util.Calendar;
+
+/**
+ * Description: validate a date or calendar representing a date in the future <br/>
+ */
+public class FutureValidatorForCalendar implements ConstraintValidator<Future, Calendar> {
+
+ public void initialize(Future annotation) {
+ }
+
+ public boolean isValid(Calendar cal, ConstraintValidatorContext context) {
+ return cal == null || cal.after(now());
+ }
+
+
+ /**
+ * overwrite when you need a different algorithm for 'now'.
+ *
+ * @return current date/time
+ */
+ protected Calendar now() {
+ return Calendar.getInstance();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForDate.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForDate.java
new file mode 100644
index 0000000..7dcd169
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForDate.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Future;
+import java.util.Date;
+
+/**
+ * Description: validate a date or calendar representing a date in the future <br/>
+ */
+public class FutureValidatorForDate implements ConstraintValidator<Future, Date> {
+
+ public void initialize(Future annotation) {
+ }
+
+ public boolean isValid(Date date, ConstraintValidatorContext context) {
+ return date == null || date.after(now());
+ }
+
+ /**
+ * overwrite when you need a different algorithm for 'now'.
+ *
+ * @return current date/time
+ */
+ protected Date now() {
+ return new Date();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
new file mode 100644
index 0000000..deaf9ea
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Max;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Check that the number being validated is less than or equal to the maximum
+ * value specified.
+ */
+public class MaxValidatorForNumber implements ConstraintValidator<Max, Number> {
+
+ private long max;
+
+ public void initialize(Max annotation) {
+ this.max = annotation.value();
+ }
+
+ public boolean isValid(Number value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ } else if (value instanceof BigDecimal) {
+ return ((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) != 1;
+ } else if (value instanceof BigInteger) {
+ return ((BigInteger) value).compareTo(BigInteger.valueOf(max)) != 1;
+ } else {
+ return value.longValue() <= max;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
new file mode 100644
index 0000000..44a2bcd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Max;
+import java.math.BigDecimal;
+
+/**
+ * Check that the String being validated represents a number, and has a value
+ * less than or equal to the maximum value specified.
+ */
+public class MaxValidatorForString implements ConstraintValidator<Max, String> {
+
+ private long max;
+
+ public void initialize(Max annotation) {
+ this.max = annotation.value();
+ }
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ }
+ try {
+ return new BigDecimal(value).compareTo(BigDecimal.valueOf(max)) != 1;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
new file mode 100644
index 0000000..7c537b9
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Min;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Description: validate that number-value of passed object is >= min-value<br/>
+ */
+public class MinValidatorForNumber implements ConstraintValidator<Min, Number> {
+
+ private long minValue;
+
+ public void initialize(Min annotation) {
+ this.minValue = annotation.value();
+ }
+
+ public boolean isValid(Number value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ } else if (value instanceof BigDecimal) {
+ return ((BigDecimal) value).compareTo(BigDecimal.valueOf(minValue)) != -1;
+ } else if (value instanceof BigInteger) {
+ return ((BigInteger) value).compareTo(BigInteger.valueOf(minValue)) != -1;
+ } else {
+ return value.longValue() >= minValue;
+ }
+
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
new file mode 100644
index 0000000..be268dd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Min;
+import java.math.BigDecimal;
+
+/**
+ * Check that the String being validated represents a number, and has a value
+ * more than or equal to the minimum value specified.
+ */
+public class MinValidatorForString implements ConstraintValidator<Min, String> {
+
+ private long minValue;
+
+ public void initialize(Min annotation) {
+ this.minValue = annotation.value();
+ }
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ }
+ try {
+ return new BigDecimal(value).compareTo(BigDecimal.valueOf(minValue)) != -1;
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmpty.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmpty.java
new file mode 100644
index 0000000..a517873
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmpty.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <pre>
+ * This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * </pre>
+ */
+@Documented
+@Constraint(
+ validatedBy = {NotEmptyValidatorForCollection.class, NotEmptyValidatorForMap.class,
+ NotEmptyValidatorForString.class, NotEmptyValidator.class})
+@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER})
+@Retention(RUNTIME)
+public @interface NotEmpty {
+ Class<?>[] groups() default {};
+
+ String message() default "{org.apache.bval.constraints.NotEmpty.message}";
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
new file mode 100644
index 0000000..2cb2491
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Description: Check the non emptyness of an
+ * any object that has a public isEmpty():boolean or a valid toString() method
+ */
+public class NotEmptyValidator implements ConstraintValidator<NotEmpty, Object> {
+ public void initialize(NotEmpty constraintAnnotation) {
+ // do nothing
+ }
+
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ if (value == null) return true;
+ if (value.getClass().isArray()) {
+ return Array.getLength(value) > 0;
+ } else {
+ try {
+ Method isEmptyMethod = value.getClass().getMethod("isEmpty");
+ if (isEmptyMethod != null) {
+ return !((Boolean) isEmptyMethod.invoke(value)).booleanValue();
+ }
+ } catch (IllegalAccessException iae) {
+ // do nothing
+ } catch (NoSuchMethodException nsme) {
+ // do nothing
+ } catch (InvocationTargetException ite) {
+ // do nothing
+ }
+ return value.toString().length() > 0;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
new file mode 100644
index 0000000..8cc0f3a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Collection;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForCollection implements ConstraintValidator<NotEmpty, Collection<?>> {
+ public void initialize(NotEmpty constraintAnnotation) {
+ // do nothing
+ }
+
+ public boolean isValid(Collection<?> value, ConstraintValidatorContext context) {
+ return value == null || !value.isEmpty();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
new file mode 100644
index 0000000..cca28bd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Map;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForMap implements ConstraintValidator<NotEmpty, Map<?, ?>> {
+ public void initialize(NotEmpty constraintAnnotation) {
+ // do nothing
+ }
+
+ public boolean isValid(Map<?, ?> value, ConstraintValidatorContext context) {
+ return value == null || !value.isEmpty();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
new file mode 100644
index 0000000..2775c8e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForString implements ConstraintValidator<NotEmpty, String> {
+ public void initialize(NotEmpty constraintAnnotation) {
+ // do nothing
+ }
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ return value == null || value.length() > 0;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotNullValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotNullValidator.java
new file mode 100644
index 0000000..913dc22
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotNullValidator.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.NotNull;
+
+/** valid when object is NOT null */
+public class NotNullValidator implements ConstraintValidator<NotNull, Object> {
+ public void initialize(NotNull constraintAnnotation) {
+ // do nothing
+ }
+
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ return value != null;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NullValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NullValidator.java
new file mode 100644
index 0000000..0dfa85f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NullValidator.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Null;
+
+/**
+ * Description: valid when object is null<br/>
+ */
+public class NullValidator implements ConstraintValidator<Null, Object> {
+
+ public void initialize(Null annotation) {
+ // do nothing
+ }
+
+ public boolean isValid(Object object, ConstraintValidatorContext context) {
+ return object == null;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForCalendar.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForCalendar.java
new file mode 100644
index 0000000..fee0dc3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForCalendar.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Past;
+import java.util.Calendar;
+
+/**
+ * Description: validate a date or calendar representing a date in the past<br/>
+ */
+public class PastValidatorForCalendar implements ConstraintValidator<Past, Calendar> {
+
+ public void initialize(Past annotation) {
+ }
+
+ public boolean isValid(Calendar cal, ConstraintValidatorContext context) {
+ return cal == null || cal.before(now());
+ }
+
+
+ /**
+ * overwrite when you need a different algorithm for 'now'.
+ *
+ * @return current date/time
+ */
+ protected Calendar now() {
+ return Calendar.getInstance();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForDate.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForDate.java
new file mode 100644
index 0000000..78d88d1
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForDate.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Past;
+import java.util.Date;
+
+/**
+ * Description: validate a date or calendar representing a date in the past<br/>
+ */
+public class PastValidatorForDate implements ConstraintValidator<Past, Date> {
+
+ public void initialize(Past annotation) {
+ }
+
+ public boolean isValid(Date date, ConstraintValidatorContext context) {
+ return date == null || date.before(now());
+ }
+
+ /**
+ * overwrite when you need a different algorithm for 'now'.
+ *
+ * @return current date/time
+ */
+ protected Date now() {
+ return new Date();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PatternValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PatternValidator.java
new file mode 100644
index 0000000..8d73561
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PatternValidator.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * validator using a regular expression,
+ * based on the jsr303 Pattern constraint annotation.
+ */
+public class PatternValidator implements ConstraintValidator<Pattern, String> {
+ protected java.util.regex.Pattern pattern;
+
+ public void initialize(Pattern annotation) {
+ Pattern.Flag flags[] = annotation.flags();
+ int intFlag = 0;
+ for (Pattern.Flag flag : flags) {
+ intFlag = intFlag | flag.getValue();
+ }
+
+ try {
+ pattern = java.util.regex.Pattern.compile(annotation.regexp(), intFlag);
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException("Invalid regular expression.", e);
+ }
+ }
+
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ return value == null || pattern.matcher(value).matches();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidator.java
new file mode 100644
index 0000000..7dc68fd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ValidationException;
+import javax.validation.constraints.Size;
+
+/**
+ * Description: Abstract validator impl. for @Size annotation<br/>
+ */
+public abstract class SizeValidator {
+ protected int min;
+ protected int max;
+
+ /**
+ * Configure the constraint validator based on the elements
+ * specified at the time it was defined.
+ *
+ * @param constraint the constraint definition
+ */
+ public void initialize(Size constraint) {
+ min = constraint.min();
+ max = constraint.max();
+ if (min < 0) throw new ValidationException("Min cannot be negative");
+ if (max < 0) throw new ValidationException("Max cannot be negative");
+ if (max < min) throw new ValidationException("Max cannot be less than Min");
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfBoolean.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfBoolean.java
new file mode 100644
index 0000000..b4abf4a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfBoolean.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfBoolean extends SizeValidator
+ implements ConstraintValidator<Size, boolean[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(boolean[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfByte.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfByte.java
new file mode 100644
index 0000000..8ee17ce
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfByte.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfByte extends SizeValidator
+ implements ConstraintValidator<Size, byte[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(byte[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfChar.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfChar.java
new file mode 100644
index 0000000..1466165
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfChar.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfChar extends SizeValidator
+ implements ConstraintValidator<Size, char[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(char[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfDouble.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfDouble.java
new file mode 100644
index 0000000..4676e5f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfDouble.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfDouble extends SizeValidator
+ implements ConstraintValidator<Size, double[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(double[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfFloat.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfFloat.java
new file mode 100644
index 0000000..71b3b64
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfFloat.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfFloat extends SizeValidator
+ implements ConstraintValidator<Size, float[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(float[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfInt.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfInt.java
new file mode 100644
index 0000000..ba2f6f3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfInt.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfInt extends SizeValidator
+ implements ConstraintValidator<Size, int[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(int[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfLong.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfLong.java
new file mode 100644
index 0000000..2965ceb
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfLong.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfLong extends SizeValidator
+ implements ConstraintValidator<Size, long[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(long[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfObject.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfObject.java
new file mode 100644
index 0000000..16a450c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfObject.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+/** Check that the length of an array is between <i>min</i> and <i>max</i> */
+public class SizeValidatorForArrayOfObject extends SizeValidator
+ implements ConstraintValidator<Size, Object[]> {
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(Object[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfShort.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfShort.java
new file mode 100644
index 0000000..df42faa
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfShort.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfShort extends SizeValidator
+ implements ConstraintValidator<Size, short[]> {
+
+ /**
+ * Checks the number of entries in an array.
+ *
+ * @param array The array to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+ * <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(short[] array, ConstraintValidatorContext context) {
+ if (array == null) {
+ return true;
+ }
+ int length = Array.getLength(array);
+ return length >= min && length <= max;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForCollection.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForCollection.java
new file mode 100644
index 0000000..f77a301
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForCollection.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.util.Collection;
+
+/** Check that a Collection's size is between min and max. */
+public class SizeValidatorForCollection extends SizeValidator
+ implements ConstraintValidator<Size, Collection<?>> {
+
+ /**
+ * Checks the number of entries in a map.
+ *
+ * @param collection The collection to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the collection is <code>null</code> or the number of entries in
+ * <code>collection</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(Collection<?> collection, ConstraintValidatorContext context) {
+ if (collection == null) {
+ return true;
+ }
+ int length = collection.size();
+ return length >= min && length <= max;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForMap.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForMap.java
new file mode 100644
index 0000000..7d73cc3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForMap.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.util.Map;
+
+/** Check that a Map's size is between min and max. */
+public class SizeValidatorForMap extends SizeValidator
+ implements ConstraintValidator<Size, Map<?, ?>> {
+ /**
+ * Checks the number of entries in a map.
+ *
+ * @param map The map to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the map is <code>null</code> or the number of entries in <code>map</code>
+ * is between the specified <code>min</code> and <code>max</code> values (inclusive),
+ * <code>false</code> otherwise.
+ */
+ public boolean isValid(Map<?, ?> map, ConstraintValidatorContext context) {
+ if (map == null) {
+ return true;
+ }
+ int size = map.size();
+ return size >= min && size <= max;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForString.java
new file mode 100644
index 0000000..53324c9
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForString.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+
+/** Check that a string's length is between min and max. */
+public class SizeValidatorForString extends SizeValidator
+ implements ConstraintValidator<Size, String> {
+ /**
+ * Checks the length of the specified string.
+ *
+ * @param s The string to validate.
+ * @param context context in which the constraint is evaluated.
+ * @return Returns <code>true</code> if the string is <code>null</code> or the length of <code>s</code> between the specified
+ * <code>min</code> and <code>max</code> values (inclusive), <code>false</code> otherwise.
+ */
+ public boolean isValid(String s, ConstraintValidatorContext context) {
+ if (s == null) {
+ return true;
+ }
+ int length = s.length();
+ return length >= min && length <= max;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
new file mode 100644
index 0000000..d6bf24d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintValidator;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.jsr303.xml.AnnotationProxyBuilder;
+import org.apache.bval.util.AccessStrategy;
+
+/**
+ * Description: helper class that builds a {@link ConstraintValidation} or its
+ * composite constraint validations by parsing the jsr303-annotations and
+ * providing information (e.g. for @OverridesAttributes) <br/>
+ */
+final class AnnotationConstraintBuilder<A extends Annotation> {
+ private static final Logger log = Logger.getLogger(AnnotationConstraintBuilder.class.getName());
+
+ private final ConstraintValidation<?> constraintValidation;
+ private List<ConstraintOverrides> overrides;
+
+ /**
+ * Create a new AnnotationConstraintBuilder instance.
+ *
+ * @param validatorClasses
+ * @param constraintValidator
+ * @param annotation
+ * @param owner
+ * @param access
+ */
+ public AnnotationConstraintBuilder(Class<? extends ConstraintValidator<A, ?>>[] validatorClasses,
+ ConstraintValidator<A, ?> constraintValidator, A annotation, Class<?> owner, AccessStrategy access) {
+ boolean reportFromComposite =
+ annotation != null && annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
+ constraintValidation =
+ new ConstraintValidation<A>(validatorClasses, constraintValidator, annotation, owner, access,
+ reportFromComposite);
+ buildFromAnnotation();
+ }
+
+ /** build attributes, payload, groups from 'annotation' */
+ private void buildFromAnnotation() {
+ if (constraintValidation.getAnnotation() != null) {
+ run(new PrivilegedAction<Object>() {
+ public Object run() {
+ for (Method method : constraintValidation.getAnnotation().annotationType().getDeclaredMethods()) {
+ // groups + payload must also appear in attributes (also
+ // checked by TCK-Tests)
+ if (method.getParameterTypes().length == 0) {
+ try {
+ if (ConstraintAnnotationAttributes.PAYLOAD.getAttributeName().equals(method.getName())) {
+ buildPayload(method);
+ } else if (ConstraintAnnotationAttributes.GROUPS.getAttributeName().equals(
+ method.getName())) {
+ buildGroups(method);
+ } else {
+ constraintValidation.getAttributes().put(method.getName(),
+ method.invoke(constraintValidation.getAnnotation()));
+ }
+ } catch (Exception e) { // do nothing
+ log.log(Level.WARNING, String.format("Error processing annotation: %s ", constraintValidation.getAnnotation()), e);
+ }
+ }
+ }
+ return null;
+ }
+ });
+ }
+ }
+
+ private void buildGroups(Method method) throws IllegalAccessException, InvocationTargetException {
+ Object raw = method.invoke(constraintValidation.getAnnotation());
+ Class<?>[] garr;
+ if (raw instanceof Class<?>) {
+ garr = new Class[] { (Class<?>) raw };
+ } else if (raw instanceof Class<?>[]) {
+ garr = (Class<?>[]) raw;
+ } else {
+ garr = null;
+ }
+
+ if (garr == null || garr.length == 0) {
+ garr = GroupsComputer.getDefaultGroupArray();
+ }
+ constraintValidation.setGroups(new HashSet<Class<?>>(Arrays.asList(garr)));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void buildPayload(Method method) throws IllegalAccessException, InvocationTargetException {
+ Class<? extends Payload>[] payload_raw =
+ (Class<? extends Payload>[]) method.invoke(constraintValidation.getAnnotation());
+ Set<Class<? extends Payload>> payloadSet;
+ if (payload_raw == null) {
+ payloadSet = Collections.<Class<? extends Payload>> emptySet();
+ } else {
+ payloadSet = new HashSet<Class<? extends Payload>>(payload_raw.length);
+ payloadSet.addAll(Arrays.asList(payload_raw));
+ }
+ constraintValidation.setPayload(payloadSet);
+ }
+
+ /**
+ * Get the configured {@link ConstraintValidation}.
+ *
+ * @return {@link ConstraintValidation}
+ */
+ public ConstraintValidation<?> getConstraintValidation() {
+ return constraintValidation;
+ }
+
+ /**
+ * initialize a child composite 'validation' with @OverridesAttribute from
+ * 'constraintValidation' and add to composites.
+ */
+ public void addComposed(ConstraintValidation<?> composite) {
+ applyOverridesAttributes(composite);
+ constraintValidation.addComposed(composite); // add AFTER apply()
+ }
+
+ private void applyOverridesAttributes(ConstraintValidation<?> composite) {
+ if (null == overrides) {
+ buildOverridesAttributes();
+ }
+ if (!overrides.isEmpty()) {
+ int index = computeIndex(composite);
+
+ // Search for the overrides to apply
+ ConstraintOverrides generalOverride = findOverride(composite.getAnnotation().annotationType(), -1);
+ if (generalOverride != null) {
+ if (index > 0) {
+ throw new ConstraintDeclarationException("Wrong OverridesAttribute declaration for "
+ + generalOverride.constraintType
+ + ", it needs a defined index when there is a list of constraints");
+ }
+ generalOverride.applyOn(composite);
+ }
+
+ ConstraintOverrides override = findOverride(composite.getAnnotation().annotationType(), index);
+ if (override != null) {
+ override.applyOn(composite);
+ }
+
+ }
+ }
+
+ /**
+ * Calculates the index of the composite constraint. The index represents
+ * the order in which it is added in reference to other constraints of the
+ * same type.
+ *
+ * @param composite
+ * The composite constraint (not yet added).
+ * @return An integer index always >= 0
+ */
+ private int computeIndex(ConstraintValidation<?> composite) {
+ int idx = 0;
+ for (ConstraintValidation<?> each : constraintValidation.getComposingValidations()) {
+ if (each.getAnnotation().annotationType() == composite.getAnnotation().annotationType()) {
+ idx++;
+ }
+ }
+ return idx;
+ }
+
+ /** read overridesAttributes from constraintValidation.annotation */
+ private void buildOverridesAttributes() {
+ overrides = new LinkedList<ConstraintOverrides>();
+ for (Method method : constraintValidation.getAnnotation().annotationType().getDeclaredMethods()) {
+ OverridesAttribute.List annoOAL = method.getAnnotation(OverridesAttribute.List.class);
+ if (annoOAL != null) {
+ for (OverridesAttribute annoOA : annoOAL.value()) {
+ parseConstraintOverride(method.getName(), annoOA);
+ }
+ }
+ OverridesAttribute annoOA = method.getAnnotation(OverridesAttribute.class);
+ if (annoOA != null) {
+ parseConstraintOverride(method.getName(), annoOA);
+ }
+ }
+ }
+
+ private void parseConstraintOverride(String methodName, OverridesAttribute oa) {
+ ConstraintOverrides target = findOverride(oa.constraint(), oa.constraintIndex());
+ if (target == null) {
+ target = new ConstraintOverrides(oa.constraint(), oa.constraintIndex());
+ overrides.add(target);
+ }
+ target.values.put(oa.name(), constraintValidation.getAttributes().get(methodName));
+ }
+
+ private ConstraintOverrides findOverride(Class<? extends Annotation> constraint, int constraintIndex) {
+ for (ConstraintOverrides each : overrides) {
+ if (each.constraintType == constraint && each.constraintIndex == constraintIndex) {
+ return each;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Holds the values to override in a composed constraint during creation of
+ * a composed ConstraintValidation
+ */
+ private static final class ConstraintOverrides {
+ final Class<? extends Annotation> constraintType;
+ final int constraintIndex;
+
+ /** key = attributeName, value = overridden value */
+ final Map<String, Object> values;
+
+ private ConstraintOverrides(Class<? extends Annotation> constraintType, int constraintIndex) {
+ this.constraintType = constraintType;
+ this.constraintIndex = constraintIndex;
+ values = new HashMap<String, Object>();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void applyOn(ConstraintValidation<?> composite) {
+ // Update the attributes
+ composite.getAttributes().putAll(values);
+
+ // And the annotation
+ Annotation originalAnnot = composite.getAnnotation();
+ AnnotationProxyBuilder<Annotation> apb = new AnnotationProxyBuilder<Annotation>(originalAnnot);
+ for (String key : values.keySet()) {
+ apb.putValue(key, values.get(key));
+ }
+ Annotation newAnnot = apb.createAnnotation();
+ ((ConstraintValidation<Annotation>) composite).setAnnotation(newAnnot);
+ }
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
new file mode 100644
index 0000000..5629e80
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
@@ -0,0 +1,450 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+
+import org.apache.bval.jsr303.util.ConstraintDefinitionValidator;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * Description: implements uniform handling of JSR303 {@link Constraint}
+ * annotations, including composed constraints and the resolution of
+ * {@link ConstraintValidator} implementations.
+ *
+ * @version $Rev: 996992 $ $Date: 2010-09-14 12:05:40 -0500 (Tue, 14 Sep 2010) $
+ */
+public final class AnnotationProcessor {
+ /** {@link ApacheFactoryContext} used */
+ private final ApacheFactoryContext factoryContext;
+
+ /**
+ * Create a new {@link AnnotationProcessor} instance.
+ *
+ * @param factoryContext
+ */
+ public AnnotationProcessor(ApacheFactoryContext factoryContext) {
+ this.factoryContext = factoryContext;
+ }
+
+ /**
+ * Process JSR303 annotations.
+ *
+ * @param prop
+ * potentially null
+ * @param owner
+ * bean type
+ * @param element
+ * whose annotations to read
+ * @param access
+ * strategy for <code>prop</code>
+ * @param appender
+ * handling accumulation
+ * @return whether any processing took place
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ public boolean processAnnotations(MetaProperty prop, Class<?> owner, AnnotatedElement element,
+ AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+
+ boolean changed = false;
+ for (Annotation annotation : element.getDeclaredAnnotations()) {
+ changed |= processAnnotation(annotation, prop, owner, access, appender);
+ }
+ return changed;
+ }
+
+ /**
+ * Convenience method to process a single class-level annotation.
+ *
+ * @param <A>
+ * annotation type
+ * @param annotation
+ * to process
+ * @param owner
+ * bean type
+ * @param appender
+ * handling accumulation
+ * @return whether any processing took place
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ public final <A extends Annotation> boolean processAnnotation(A annotation, Class<?> owner, AppendValidation appender)
+ throws IllegalAccessException, InvocationTargetException {
+ return processAnnotation(annotation, null, owner, null, appender);
+ }
+
+ /**
+ * Process a single annotation.
+ *
+ * @param <A>
+ * annotation type
+ * @param annotation
+ * to process
+ * @param prop
+ * potentially null
+ * @param owner
+ * bean type
+ * @param access
+ * strategy for <code>prop</code>
+ * @param appender
+ * handling accumulation
+ * @return whether any processing took place
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ public <A extends Annotation> boolean processAnnotation(A annotation, MetaProperty prop, Class<?> owner,
+ AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+ if (annotation instanceof Valid) {
+ return addAccessStrategy(prop, access);
+ }
+ /**
+ * An annotation is considered a constraint definition if its retention
+ * policy contains RUNTIME and if the annotation itself is annotated
+ * with javax.validation.Constraint.
+ */
+ Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+ if (vcAnno != null) {
+ ConstraintDefinitionValidator.validateConstraintDefinition(annotation);
+ Class<? extends ConstraintValidator<A, ?>>[] validatorClasses;
+ validatorClasses = findConstraintValidatorClasses(annotation, vcAnno);
+ return applyConstraint(annotation, validatorClasses, prop, owner, access, appender);
+ }
+ /**
+ * Multi-valued constraints: To support this requirement, the bean
+ * validation provider treats regular annotations (annotations not
+ * annotated by @Constraint) whose value element has a return type of an
+ * array of constraint annotations in a special way.
+ */
+ Object result =
+ SecureActions.getAnnotationValue(annotation, ConstraintAnnotationAttributes.VALUE.getAttributeName());
+ if (result instanceof Annotation[]) {
+ boolean changed = false;
+ for (Annotation each : (Annotation[]) result) {
+ changed |= processAnnotation(each, prop, owner, access, appender);
+ }
+ return changed;
+ }
+ return false;
+ }
+
+ /**
+ * Add the specified {@link AccessStrategy} to <code>prop</code>; noop if
+ * <code>prop == null</code>.
+ *
+ * @param prop
+ * @param access
+ * @return whether anything took place.
+ */
+ public boolean addAccessStrategy(MetaProperty prop, AccessStrategy access) {
+ if (prop == null) {
+ return false;
+ }
+ AccessStrategy[] strategies = prop.getFeature(Features.Property.REF_CASCADE);
+ if (strategies == null) {
+ strategies = new AccessStrategy[] { access };
+ prop.putFeature(Features.Property.REF_CASCADE, strategies);
+ } else if (!ArrayUtils.contains(strategies, access)) {
+ prop.putFeature(Features.Property.REF_CASCADE, ArrayUtils.add(strategies, access));
+ }
+ return true;
+ }
+
+ /**
+ * Find available {@link ConstraintValidation} classes for a given
+ * constraint annotation.
+ *
+ * @param annotation
+ * @param vcAnno
+ * @return {@link ConstraintValidation} implementation class array
+ */
+ @SuppressWarnings("unchecked")
+ private <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] findConstraintValidatorClasses(
+ A annotation, Constraint vcAnno) {
+ if (vcAnno == null) {
+ vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+ }
+ Class<? extends ConstraintValidator<A, ?>>[] validatorClasses;
+ Class<A> annotationType = (Class<A>) annotation.annotationType();
+ validatorClasses = factoryContext.getFactory().getConstraintsCache().getConstraintValidators(annotationType);
+ if (validatorClasses == null) {
+ validatorClasses = (Class<? extends ConstraintValidator<A, ?>>[]) vcAnno.validatedBy();
+ if (validatorClasses.length == 0) {
+ validatorClasses =
+ factoryContext.getFactory().getDefaultConstraints().getValidatorClasses(annotationType);
+ }
+ }
+ return validatorClasses;
+ }
+
+ /**
+ * Apply a constraint to the specified <code>appender</code>.
+ *
+ * @param annotation
+ * constraint annotation
+ * @param constraintClasses
+ * known {@link ConstraintValidator} implementation classes for
+ * <code>annotation</code>
+ * @param prop
+ * meta-property
+ * @param owner
+ * type
+ * @param access
+ * strategy
+ * @param appender
+ * @return success flag
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ private <A extends Annotation> boolean applyConstraint(A annotation,
+ Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, MetaProperty prop, Class<?> owner,
+ AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+
+ final ConstraintValidator<A, ?> validator =
+ getConstraintValidator(annotation, constraintClasses, owner, access);
+ final AnnotationConstraintBuilder<A> builder =
+ new AnnotationConstraintBuilder<A>(constraintClasses, validator, annotation, owner, access);
+
+ // JSR-303 3.4.4: Add implicit groups
+ if (prop != null && prop.getParentMetaBean() != null) {
+ MetaBean parentMetaBean = prop.getParentMetaBean();
+ // If:
+ // - the owner is an interface
+ // - the class of the metabean being build is different than the
+ // owner
+ // - and only the Default group is defined
+ // Then: add the owner interface as implicit groups
+ if (builder.getConstraintValidation().getOwner().isInterface()
+ && parentMetaBean.getBeanClass() != builder.getConstraintValidation().getOwner()
+ && builder.getConstraintValidation().getGroups().size() == 1
+ && builder.getConstraintValidation().getGroups().contains(Default.class)) {
+ Set<Class<?>> groups = builder.getConstraintValidation().getGroups();
+ groups.add(builder.getConstraintValidation().getOwner());
+ builder.getConstraintValidation().setGroups(groups);
+ }
+ }
+
+ // If already building a constraint composition tree, ensure that:
+ // - the parent groups are inherited
+ // - the parent payload is inherited
+ if (appender instanceof AppendValidationToBuilder) {
+ AppendValidationToBuilder avb = (AppendValidationToBuilder) appender;
+ builder.getConstraintValidation().setGroups(avb.getInheritedGroups());
+ builder.getConstraintValidation().setPayload(avb.getInheritedPayload());
+ }
+
+ // process composed constraints:
+ // here are not other superclasses possible, because annotations do not
+ // inherit!
+ processAnnotations(prop, owner, annotation.annotationType(), access, new AppendValidationToBuilder(builder));
+
+ // Even if the validator is null, it must be added to mimic the RI impl
+ appender.append(builder.getConstraintValidation());
+ return true;
+ }
+
+ private <A extends Annotation, T> ConstraintValidator<A, ? super T> getConstraintValidator(A annotation,
+ Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, Class<?> owner, AccessStrategy access) {
+ if (constraintClasses != null && constraintClasses.length > 0) {
+ Type type = determineTargetedType(owner, access);
+ /**
+ * spec says in chapter 3.5.3.: The ConstraintValidator chosen to
+ * validate a declared type T is the one where the type supported by
+ * the ConstraintValidator is a supertype of T and where there is no
+ * other ConstraintValidator whose supported type is a supertype of
+ * T and not a supertype of the chosen ConstraintValidator supported
+ * type.
+ */
+ Map<Type, Class<? extends ConstraintValidator<A, ?>>> validatorTypes =
+ getValidatorsTypes(constraintClasses);
+ final List<Type> assignableTypes = new ArrayList<Type>(constraintClasses.length);
+ fillAssignableTypes(type, validatorTypes.keySet(), assignableTypes);
+ reduceAssignableTypes(assignableTypes);
+ checkOneType(assignableTypes, type, owner, annotation, access);
+
+ @SuppressWarnings("unchecked")
+ final ConstraintValidator<A, ? super T> validator =
+ (ConstraintValidator<A, ? super T>) factoryContext.getFactory().getConstraintValidatorFactory()
+ .getInstance(validatorTypes.get(assignableTypes.get(0)));
+ if (validator == null) {
+ throw new ValidationException("Factory returned null validator for: "
+ + validatorTypes.get(assignableTypes.get(0)));
+
+ }
+ return validator;
+ // NOTE: validator initialization deferred until append phase
+ }
+ return null;
+ }
+
+ private static void checkOneType(List<Type> types, Type targetType, Class<?> owner, Annotation anno,
+ AccessStrategy access) {
+
+ if (types.isEmpty()) {
+ StringBuilder buf =
+ new StringBuilder().append("No validator could be found for type ").append(stringForType(targetType))
+ .append(". See: @").append(anno.annotationType().getSimpleName()).append(" at ").append(
+ stringForLocation(owner, access));
+ throw new UnexpectedTypeException(buf.toString());
+ } else if (types.size() > 1) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("Ambiguous validators for type ");
+ buf.append(stringForType(targetType));
+ buf.append(". See: @").append(anno.annotationType().getSimpleName()).append(" at ").append(
+ stringForLocation(owner, access));
+ buf.append(". Validators are: ");
+ boolean comma = false;
+ for (Type each : types) {
+ if (comma)
+ buf.append(", ");
+ comma = true;
+ buf.append(each);
+ }
+ throw new UnexpectedTypeException(buf.toString());
+ }
+ }
+
+ /** implements spec chapter 3.5.3. ConstraintValidator resolution algorithm. */
+ private static Type determineTargetedType(Class<?> owner, AccessStrategy access) {
+ // if the constraint declaration is hosted on a class or an interface,
+ // the targeted type is the class or the interface.
+ if (access == null)
+ return owner;
+ Type type = access.getJavaType();
+ if (type == null)
+ return Object.class;
+ if (type instanceof Class<?>)
+ type = ClassUtils.primitiveToWrapper((Class<?>) type);
+ return type;
+ }
+
+ private static String stringForType(Type clazz) {
+ if (clazz instanceof Class<?>) {
+ if (((Class<?>) clazz).isArray()) {
+ return ((Class<?>) clazz).getComponentType().getName() + "[]";
+ } else {
+ return ((Class<?>) clazz).getName();
+ }
+ } else {
+ return clazz.toString();
+ }
+ }
+
+ private static String stringForLocation(Class<?> owner, AccessStrategy access) {
+ if (access != null) {
+ return access.toString();
+ } else {
+ return owner.getName();
+ }
+ }
+
+ private static void fillAssignableTypes(Type type, Set<Type> validatorsTypes, List<Type> suitableTypes) {
+ for (Type validatorType : validatorsTypes) {
+ if (org.apache.commons.lang3.reflect.TypeUtils.isAssignable(type, validatorType)
+ && !suitableTypes.contains(validatorType)) {
+ suitableTypes.add(validatorType);
+ }
+ }
+ }
+
+ /**
+ * Tries to reduce all assignable classes down to a single class.
+ *
+ * @param assignableTypes
+ * The set of all classes which are assignable to the class of
+ * the value to be validated and which are handled by at least
+ * one of the validators for the specified constraint.
+ */
+ private static void reduceAssignableTypes(List<Type> assignableTypes) {
+ if (assignableTypes.size() <= 1) {
+ return; // no need to reduce
+ }
+ boolean removed;
+ do {
+ removed = false;
+ final Type type = assignableTypes.get(0);
+ for (int i = 1; i < assignableTypes.size(); i++) {
+ Type nextType = assignableTypes.get(i);
+ if (TypeUtils.isAssignable(nextType, type)) {
+ assignableTypes.remove(0);
+ i--;
+ removed = true;
+ } else if (TypeUtils.isAssignable(type, nextType)) {
+ assignableTypes.remove(i--);
+ removed = true;
+ }
+ }
+ } while (removed && assignableTypes.size() > 1);
+
+ }
+
+ /**
+ * Given a set of {@link ConstraintValidator} implementation classes, map
+ * those to their target types.
+ *
+ * @param constraintValidatorClasses
+ * @return {@link Map} of {@link Type} : {@link ConstraintValidator} subtype
+ */
+ private static <A extends Annotation> Map<Type, Class<? extends ConstraintValidator<A, ?>>> getValidatorsTypes(
+ Class<? extends ConstraintValidator<A, ?>>[] constraintValidatorClasses) {
+ Map<Type, Class<? extends ConstraintValidator<A, ?>>> validatorsTypes =
+ new HashMap<Type, Class<? extends ConstraintValidator<A, ?>>>();
+ for (Class<? extends ConstraintValidator<A, ?>> validatorType : constraintValidatorClasses) {
+ Type validatedType =
+ TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class).get(
+ ConstraintValidator.class.getTypeParameters()[1]);
+ if (validatedType == null) {
+ throw new ValidationException(String.format("Could not detect validated type for %s", validatorType));
+ }
+ if (validatedType instanceof GenericArrayType) {
+ Type componentType = TypeUtils.getArrayComponentType(validatedType);
+ if (componentType instanceof Class<?>) {
+ validatedType = Array.newInstance((Class<?>) componentType, 0).getClass();
+ }
+ }
+ validatorsTypes.put(validatedType, validatorType);
+ }
+ return validatorsTypes;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java
new file mode 100644
index 0000000..8072f59
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorContext;
+
+import org.apache.bval.IntrospectorMetaBeanFactory;
+import org.apache.bval.MetaBeanBuilder;
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.MetaBeanManager;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.xml.XMLMetaBeanBuilder;
+import org.apache.bval.xml.XMLMetaBeanFactory;
+import org.apache.bval.xml.XMLMetaBeanManager;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.ConstructorUtils;
+
+/**
+ * Description: Represents the context that is used to create
+ * <code>ClassValidator</code> instances.<br/>
+ */
+public class ApacheFactoryContext implements ValidatorContext {
+ private final ApacheValidatorFactory factory;
+ private final MetaBeanFinder metaBeanFinder;
+
+ private MessageInterpolator messageInterpolator;
+ private TraversableResolver traversableResolver;
+ private ConstraintValidatorFactory constraintValidatorFactory;
+
+ /**
+ * Create a new ApacheFactoryContext instance.
+ *
+ * @param factory
+ */
+ public ApacheFactoryContext(ApacheValidatorFactory factory) {
+ this.factory = factory;
+ this.metaBeanFinder = buildMetaBeanFinder();
+ }
+
+ /**
+ * Create a new ApacheFactoryContext instance.
+ *
+ * @param factory
+ * @param metaBeanFinder
+ */
+ protected ApacheFactoryContext(ApacheValidatorFactory factory, MetaBeanFinder metaBeanFinder) {
+ this.factory = factory;
+ this.metaBeanFinder = metaBeanFinder;
+ }
+
+ /**
+ * Get the {@link ApacheValidatorFactory} used by this
+ * {@link ApacheFactoryContext}.
+ *
+ * @return {@link ApacheValidatorFactory}
+ */
+ public ApacheValidatorFactory getFactory() {
+ return factory;
+ }
+
+ /**
+ * Get the metaBeanFinder.
+ *
+ * @return {@link MetaBeanFinder}
+ */
+ public final MetaBeanFinder getMetaBeanFinder() {
+ return metaBeanFinder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ValidatorContext messageInterpolator(MessageInterpolator messageInterpolator) {
+ this.messageInterpolator = messageInterpolator;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ValidatorContext traversableResolver(TraversableResolver traversableResolver) {
+ this.traversableResolver = traversableResolver;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ValidatorContext constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
+ this.constraintValidatorFactory = constraintValidatorFactory;
+ return this;
+ }
+
+ /**
+ * Get the {@link ConstraintValidatorFactory}.
+ *
+ * @return {@link ConstraintValidatorFactory}
+ */
+ public ConstraintValidatorFactory getConstraintValidatorFactory() {
+ return constraintValidatorFactory == null ? factory.getConstraintValidatorFactory()
+ : constraintValidatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Validator getValidator() {
+ ClassValidator validator = new ClassValidator(this);
+ if (Boolean.parseBoolean(factory.getProperties().get(
+ ApacheValidatorConfiguration.Properties.TREAT_MAPS_LIKE_BEANS))) {
+ validator.setTreatMapsLikeBeans(true);
+ }
+ return validator;
+ }
+
+ /**
+ * Get the {@link MessageInterpolator}.
+ *
+ * @return {@link MessageInterpolator}
+ */
+ public MessageInterpolator getMessageInterpolator() {
+ return messageInterpolator == null ? factory.getMessageInterpolator() : messageInterpolator;
+ }
+
+ /**
+ * Get the {@link TraversableResolver}.
+ *
+ * @return {@link TraversableResolver}
+ */
+ public TraversableResolver getTraversableResolver() {
+ return traversableResolver == null ? factory.getTraversableResolver() : traversableResolver;
+ }
+
+ /**
+ * Create MetaBeanManager that uses factories:
+ * <ol>
+ * <li>if enabled by
+ * {@link ApacheValidatorConfiguration.Properties#ENABLE_INTROSPECTOR}, an
+ * {@link IntrospectorMetaBeanFactory}</li>
+ * <li>{@link MetaBeanFactory} types (if any) specified by
+ * {@link ApacheValidatorConfiguration.Properties#METABEAN_FACTORY_CLASSNAMES}
+ * </li>
+ * <li>if no {@link Jsr303MetaBeanFactory} has yet been specified (this
+ * allows factory order customization), a {@link Jsr303MetaBeanFactory}
+ * which handles both JSR303-XML and JSR303-Annotations</li>
+ * <li>if enabled by
+ * {@link ApacheValidatorConfiguration.Properties#ENABLE_METABEANS_XML}, an
+ * {@link XMLMetaBeanFactory}</li>
+ * </ol>
+ *
+ * @return a new instance of MetaBeanManager with adequate MetaBeanFactories
+ */
+ protected MetaBeanFinder buildMetaBeanFinder() {
+ List<MetaBeanFactory> builders = new ArrayList<MetaBeanFactory>();
+ if (Boolean.parseBoolean(factory.getProperties().get(
+ ApacheValidatorConfiguration.Properties.ENABLE_INTROSPECTOR))) {
+ builders.add(new IntrospectorMetaBeanFactory());
+ }
+ String[] factoryClassNames =
+ StringUtils.split(
+ factory.getProperties().get(ApacheValidatorConfiguration.Properties.METABEAN_FACTORY_CLASSNAMES));
+ if (factoryClassNames != null) {
+ for (String clsName : factoryClassNames) {
+ // cast, relying on #createMetaBeanFactory to throw the exception if incompatible:
+ @SuppressWarnings("unchecked")
+ Class<? extends MetaBeanFactory> factoryClass = (Class<? extends MetaBeanFactory>) loadClass(clsName);
+ builders.add(createMetaBeanFactory(factoryClass));
+ }
+ }
+ boolean jsr303Found = false;
+ for (MetaBeanFactory builder : builders) {
+ jsr303Found |= builder instanceof Jsr303MetaBeanFactory;
+ }
+ if (!jsr303Found) {
+ builders.add(new Jsr303MetaBeanFactory(this));
+ }
+ @SuppressWarnings("deprecation")
+ boolean enableMetaBeansXml = Boolean.parseBoolean(factory.getProperties().get(
+ ApacheValidatorConfiguration.Properties.ENABLE_METABEANS_XML));
+ if (enableMetaBeansXml) {
+ XMLMetaBeanManagerCreator.addFactory(builders);
+ }
+ return createMetaBeanManager(builders);
+ }
+
+ /**
+ * Create a {@link MetaBeanManager} using the specified builders.
+ *
+ * @param builders
+ * {@link MetaBeanFactory} {@link List}
+ * @return {@link MetaBeanManager}
+ */
+ @SuppressWarnings("deprecation")
+ protected MetaBeanFinder createMetaBeanManager(List<MetaBeanFactory> builders) {
+ // as long as we support both: jsr303 (in the builders list) and xstream-xml metabeans:
+ if (Boolean.parseBoolean(factory.getProperties().get(
+ ApacheValidatorConfiguration.Properties.ENABLE_METABEANS_XML))) {
+ return XMLMetaBeanManagerCreator.createXMLMetaBeanManager(builders);
+ }
+ return new MetaBeanManager(new MetaBeanBuilder(builders.toArray(new MetaBeanFactory[builders.size()])));
+ }
+
+ private <F extends MetaBeanFactory> F createMetaBeanFactory(final Class<F> cls) {
+ return run(new PrivilegedAction<F>() {
+
+ public F run() {
+ try {
+ Constructor<F> c = ConstructorUtils.getMatchingAccessibleConstructor(cls, ApacheFactoryContext.this.getClass());
+ if (c != null) {
+ return c.newInstance(ApacheFactoryContext.this);
+ }
+ c = ConstructorUtils.getMatchingAccessibleConstructor(cls, getFactory().getClass());
+ if (c != null) {
+ return c.newInstance(getFactory());
+ }
+ return cls.newInstance();
+ } catch (Exception e) {
+ throw new ValidationException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * separate class to prevent the classloader to immediately load optional
+ * classes: XMLMetaBeanManager, XMLMetaBeanFactory, XMLMetaBeanBuilder that
+ * might not be available in the classpath
+ */
+ private static class XMLMetaBeanManagerCreator {
+
+ static void addFactory(List<MetaBeanFactory> builders) {
+ builders.add(new XMLMetaBeanFactory());
+ }
+
+ /**
+ * Create the {@link MetaBeanManager} to process JSR303 XML. Requires
+ * bval-xstream at RT.
+ *
+ * @param builders
+ * @return {@link MetaBeanManager}
+ */
+ // NOTE - We return MetaBeanManager instead of XMLMetaBeanManager to
+ // keep
+ // bval-xstream an optional module.
+ protected static MetaBeanManager createXMLMetaBeanManager(List<MetaBeanFactory> builders) {
+ return new XMLMetaBeanManager(
+ new XMLMetaBeanBuilder(builders.toArray(new MetaBeanFactory[builders.size()])));
+ }
+ }
+
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+ private Class<?> loadClass(final String className) {
+ ClassLoader loader = doPrivileged(SecureActions.getContextClassLoader());
+ if (loader == null)
+ loader = getClass().getClassLoader();
+
+ try {
+ return Class.forName(className, true, loader);
+ } catch (ClassNotFoundException ex) {
+ throw new ValidationException("Unable to load class: " + className, ex);
+ }
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidationProvider.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidationProvider.java
new file mode 100644
index 0000000..286016c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidationProvider.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.validation.Configuration;
+import javax.validation.ValidationException;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.apache.commons.lang3.ClassUtils;
+
+/**
+ * Description: Implementation of {@link ValidationProvider} for jsr303
+ * implementation of the apache-validation framework.
+ * <p/>
+ * <br/>
+ * User: roman.stumm <br/>
+ * Date: 29.10.2008 <br/>
+ * Time: 14:45:41 <br/>
+ */
+public class ApacheValidationProvider implements ValidationProvider<ApacheValidatorConfiguration> {
+
+ /**
+ * Learn whether a particular builder class is suitable for this
+ * {@link ValidationProvider}.
+ *
+ * @param builderClass
+ * @return boolean suitability
+ */
+ public boolean isSuitable(Class<? extends Configuration<?>> builderClass) {
+ return ApacheValidatorConfiguration.class == builderClass;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ApacheValidatorConfiguration createSpecializedConfiguration(BootstrapState state) {
+ return new ConfigurationImpl(state, this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Configuration<?> createGenericConfiguration(BootstrapState state) {
+ return new ConfigurationImpl(state, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws javax.validation.ValidationException
+ * if the ValidatorFactory cannot be built
+ */
+ @SuppressWarnings("unchecked")
+ public ValidatorFactory buildValidatorFactory(final ConfigurationState configuration) {
+ final Class<? extends ValidatorFactory> validatorFactoryClass;
+ try {
+ String validatorFactoryClassname =
+ configuration.getProperties().get(ApacheValidatorConfiguration.Properties.VALIDATOR_FACTORY_CLASSNAME);
+
+ if (validatorFactoryClassname == null)
+ validatorFactoryClass = ApacheValidatorFactory.class;
+ else
+ {
+ validatorFactoryClass
+ = (Class<? extends ValidatorFactory>) ClassUtils.getClass(validatorFactoryClassname);
+ validatorFactoryClass.asSubclass(ValidatorFactory.class);
+ }
+ } catch (ValidationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ValidationException("error building ValidatorFactory", ex);
+ }
+
+ // FIXME 2011-03-27 jw:
+ // Should not use privileged action, but to avoid breaking things
+ // doing it here like the former version of this class did.
+ //
+ // The privileged action should be applied by the ValidatorFactory
+ // itself, if required.
+ // No privileges should be required to access the constructor,
+ // because the classloader of ApacheValidationProvider will always
+ // be an ancestor of the loader of validatorFactoryClass.
+ return (System.getSecurityManager() == null)
+ ? instantiateValidatorFactory(validatorFactoryClass, configuration)
+ : AccessController.doPrivileged(new PrivilegedAction<ValidatorFactory>() {
+ public ValidatorFactory run() {
+ return instantiateValidatorFactory(validatorFactoryClass, configuration);
+ }
+ });
+ }
+
+
+
+ private static ValidatorFactory instantiateValidatorFactory(
+ final Class<? extends ValidatorFactory> validatorFactoryClass,
+ final ConfigurationState configuration
+ ) {
+ try
+ {
+ return validatorFactoryClass.getConstructor(ConfigurationState.class).newInstance(configuration);
+ }
+ catch (final Exception ex)
+ {
+ throw new ValidationException("Cannot instantiate : " + validatorFactoryClass, ex);
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java
new file mode 100644
index 0000000..8dd3ccf
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.Configuration;
+import javax.validation.ValidatorContext;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+/**
+ * Description: Uniquely identify Apache BVal in the Bean Validation bootstrap
+ * strategy. Also contains Apache BVal specific configurations<br/>
+ */
+public interface ApacheValidatorConfiguration
+ extends Configuration<ApacheValidatorConfiguration> {
+
+ /**
+ * Proprietary property keys for {@link ConfigurationImpl}
+ */
+ public interface Properties {
+ /**
+ * the location where to look for the validation.xml file.
+ * default: "META-INF/validation.xml"
+ */
+ String VALIDATION_XML_PATH = "apache.bval.validation-xml-path";
+
+ /**
+ * true/false. use Introspector (java beans) metadata additionally
+ * to build metadata with JSR303.<br>
+ * This means that all properties exist that are java-bean properties and
+ * and that some features (Hidden, Readonly) are taken from Introspector
+ * to create the meta data.<br>
+ * default: false
+ */
+ String ENABLE_INTROSPECTOR = "apache.bval.enable-introspector";
+
+ /**
+ * true/false. use Apache metaBeans xml format additionally to
+ * build metadata with JSR303.
+ * default: false
+ *
+ * @deprecated we could decide to drop this feature in the future.
+ * we keep it as long as we support both: jsr303 and xstream-xml meta data at
+ * the same time (and potentially for the same domain classes)
+ */
+ String ENABLE_METABEANS_XML = "apache.bval.enable-metabeans-xml";
+
+ /**
+ * BeanValidator.treatMapsLikeBeans.
+ * default: false
+ */
+ String TREAT_MAPS_LIKE_BEANS = "apache.bval.treat-maps-like-beans";
+
+ /**
+ * Specifies the classname of the {@link ValidatorFactory} to use: this
+ * class is presumed have a constructor that accepts a single
+ * {@link ConfigurationState} argument.
+ */
+ String VALIDATOR_FACTORY_CLASSNAME = "apache.bval.validator-factory-classname";
+
+ /**
+ * Specifies the names, delimited by whitespace, of
+ * {@link MetaBeanFactory} classes that should be added to collaborate
+ * with an {@link ApacheFactoryContext}'s {@link MetaBeanFinder}. These
+ * are instantiated per {@link ValidatorContext}, attempting to use
+ * constructor arguments of decreasing specificity:
+ * <ol>
+ * <li>assignable from the creating {@link ApacheFactoryContext}</li>
+ * <li>assignable from the associated {@link ApacheValidatorFactory}</li>
+ * <li>default (no-args) constructor</li>
+ * </ol>
+ */
+ String METABEAN_FACTORY_CLASSNAMES = "apache.bval.metabean-factory-classnames";
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java
new file mode 100644
index 0000000..891efd0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java
@@ -0,0 +1,390 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.jsr303.xml.AnnotationIgnores;
+import org.apache.bval.jsr303.xml.MetaConstraint;
+import org.apache.bval.jsr303.xml.ValidationMappingParser;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ClassUtils;
+
+import javax.validation.*;
+import javax.validation.bootstrap.ProviderSpecificBootstrap;
+import javax.validation.spi.ConfigurationState;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+/**
+ * Description: a factory is a complete configurated object that can create
+ * validators.<br/>
+ * This instance is not thread-safe.<br/>
+ */
+public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
+ private static volatile ApacheValidatorFactory DEFAULT_FACTORY;
+ private static final ConstraintDefaults defaultConstraints =
+ new ConstraintDefaults();
+
+ private MessageInterpolator messageResolver;
+ private TraversableResolver traversableResolver;
+ private ConstraintValidatorFactory constraintValidatorFactory;
+ private final Map<String, String> properties;
+
+ /**
+ * information from xml parsing
+ */
+ private final AnnotationIgnores annotationIgnores = new AnnotationIgnores();
+ private final ConstraintCached constraintsCache = new ConstraintCached();
+ private final Map<Class<?>, Class<?>[]> defaultSequences;
+ /**
+ * access strategies for properties with cascade validation @Valid support
+ */
+ private final Map<Class<?>, List<AccessStrategy>> validAccesses;
+ private final Map<Class<?>, List<MetaConstraint<?, ? extends Annotation>>> constraintMap;
+
+ /**
+ * Convenience method to retrieve a default global ApacheValidatorFactory
+ *
+ * @return {@link ApacheValidatorFactory}
+ */
+ public static synchronized ApacheValidatorFactory getDefault() {
+ if (DEFAULT_FACTORY == null) {
+ ProviderSpecificBootstrap<ApacheValidatorConfiguration> provider =
+ Validation.byProvider(ApacheValidationProvider.class);
+ ApacheValidatorConfiguration configuration = provider.configure();
+ DEFAULT_FACTORY =
+ (ApacheValidatorFactory) configuration.buildValidatorFactory();
+ }
+ return DEFAULT_FACTORY;
+ }
+
+ /**
+ * Set a particular {@link ApacheValidatorFactory} instance as the default.
+ *
+ * @param aDefaultFactory
+ */
+ public static void setDefault(ApacheValidatorFactory aDefaultFactory) {
+ DEFAULT_FACTORY = aDefaultFactory;
+ }
+
+ /**
+ * Create a new ApacheValidatorFactory instance.
+ */
+ public ApacheValidatorFactory(ConfigurationState configurationState) {
+ properties = new HashMap<String, String>();
+ defaultSequences = new HashMap<Class<?>, Class<?>[]>();
+ validAccesses = new HashMap<Class<?>, List<AccessStrategy>>();
+ constraintMap =
+ new HashMap<Class<?>, List<MetaConstraint<?, ? extends Annotation>>>();
+ configure(configurationState);
+ }
+
+ /**
+ * Configure this {@link ApacheValidatorFactory} from a
+ * {@link ConfigurationState}.
+ *
+ * @param configuration
+ */
+ protected void configure(ConfigurationState configuration) {
+ getProperties().putAll(configuration.getProperties());
+ setMessageInterpolator(configuration.getMessageInterpolator());
+ setTraversableResolver(configuration.getTraversableResolver());
+ setConstraintValidatorFactory(configuration
+ .getConstraintValidatorFactory());
+ ValidationMappingParser parser = new ValidationMappingParser(this);
+ parser.processMappingConfig(configuration.getMappingStreams());
+ }
+
+ /**
+ * Get the property map of this {@link ApacheValidatorFactory}.
+ *
+ * @return Map<String, String>
+ */
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ /**
+ * Get the default {@link MessageInterpolator} used by this
+ * {@link ApacheValidatorFactory}.
+ *
+ * @return {@link MessageInterpolator}
+ */
+ protected MessageInterpolator getDefaultMessageInterpolator() {
+ return messageResolver;
+ }
+
+ /**
+ * Shortcut method to create a new Validator instance with factory's
+ * settings
+ *
+ * @return the new validator instance
+ */
+ public Validator getValidator() {
+ return usingContext().getValidator();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the validator factory's context
+ */
+ public ApacheFactoryContext usingContext() {
+ return new ApacheFactoryContext(this);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized ApacheValidatorFactory clone() {
+ try {
+ return (ApacheValidatorFactory) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError(); // VM bug.
+ }
+ }
+
+ /**
+ * Set the {@link MessageInterpolator} used.
+ *
+ * @param messageResolver
+ */
+ public final void setMessageInterpolator(MessageInterpolator messageResolver) {
+ this.messageResolver = messageResolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MessageInterpolator getMessageInterpolator() {
+ return ((messageResolver != null) ? messageResolver
+ : getDefaultMessageInterpolator());
+ }
+
+ /**
+ * Set the {@link TraversableResolver} used.
+ *
+ * @param traversableResolver
+ */
+ public final void setTraversableResolver(
+ TraversableResolver traversableResolver) {
+ this.traversableResolver = traversableResolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TraversableResolver getTraversableResolver() {
+ return traversableResolver;
+ }
+
+ /**
+ * Set the {@link ConstraintValidatorFactory} used.
+ *
+ * @param constraintValidatorFactory
+ */
+ public final void setConstraintValidatorFactory(
+ ConstraintValidatorFactory constraintValidatorFactory) {
+ this.constraintValidatorFactory = constraintValidatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorFactory getConstraintValidatorFactory() {
+ return constraintValidatorFactory;
+ }
+
+ /**
+ * Return an object of the specified type to allow access to the
+ * provider-specific API. If the Bean Validation provider implementation
+ * does not support the specified class, the ValidationException is thrown.
+ *
+ * @param type the class of the object to be returned.
+ * @return an instance of the specified class
+ * @throws ValidationException if the provider does not support the call.
+ */
+ public <T> T unwrap(final Class<T> type) {
+ // FIXME 2011-03-27 jw:
+ // This code is unsecure.
+ // It should allow only a fixed set of classes.
+ // Can't fix this because don't know which classes this method should support.
+
+ if (type.isInstance(this)) {
+ @SuppressWarnings("unchecked")
+ T result = (T) this;
+ return result;
+ } else if (!(type.isInterface() || Modifier.isAbstract(type
+ .getModifiers()))) {
+ return newInstance(type);
+ } else {
+ try {
+ Class<?> cls = ClassUtils.getClass(type.getName() + "Impl");
+ if (type.isAssignableFrom(cls)) {
+ @SuppressWarnings("unchecked")
+ T result = (T) newInstance(cls);
+ return result;
+ }
+ } catch (ClassNotFoundException e) {
+ // do nothing
+ }
+ throw new ValidationException("Type " + type + " not supported");
+ }
+ }
+
+ private <T> T newInstance(final Class<T> cls) {
+ return AccessController.doPrivileged(new PrivilegedAction<T>() {
+ public T run() {
+ try {
+ return cls.newInstance();
+ } catch (final Exception ex) {
+ throw new ValidationException("Cannot instantiate : " + cls, ex);
+ }
+ }
+ });
+ }
+
+ /**
+ * Get the detected {@link ConstraintDefaults}.
+ *
+ * @return ConstraintDefaults
+ */
+ public ConstraintDefaults getDefaultConstraints() {
+ return defaultConstraints;
+ }
+
+ /**
+ * Get the detected {@link AnnotationIgnores}.
+ *
+ * @return AnnotationIgnores
+ */
+ public AnnotationIgnores getAnnotationIgnores() {
+ return annotationIgnores;
+ }
+
+ /**
+ * Get the constraint cache used.
+ *
+ * @return {@link ConstraintCached}
+ */
+ public ConstraintCached getConstraintsCache() {
+ return constraintsCache;
+ }
+
+ /**
+ * Add a meta-constraint to this {@link ApacheValidatorFactory}'s runtime
+ * customizations.
+ *
+ * @param beanClass
+ * @param metaConstraint
+ */
+ public void addMetaConstraint(Class<?> beanClass,
+ MetaConstraint<?, ?> metaConstraint) {
+ List<MetaConstraint<?, ? extends Annotation>> slot =
+ constraintMap.get(beanClass);
+ if (slot != null) {
+ slot.add(metaConstraint);
+ } else {
+ List<MetaConstraint<?, ? extends Annotation>> constraintList =
+ new ArrayList<MetaConstraint<?, ? extends Annotation>>();
+ constraintList.add(metaConstraint);
+ constraintMap.put(beanClass, constraintList);
+ }
+ }
+
+ /**
+ * Mark a property of <code>beanClass</code> for nested validation.
+ *
+ * @param beanClass
+ * @param accessStrategy
+ * defining the property to validate
+ */
+ public void addValid(Class<?> beanClass, AccessStrategy accessStrategy) {
+ List<AccessStrategy> slot = validAccesses.get(beanClass);
+ if (slot != null) {
+ slot.add(accessStrategy);
+ } else {
+ List<AccessStrategy> tmpList = new ArrayList<AccessStrategy>();
+ tmpList.add(accessStrategy);
+ validAccesses.put(beanClass, tmpList);
+ }
+ }
+
+ /**
+ * Set the default group sequence for a particular bean class.
+ *
+ * @param beanClass
+ * @param groupSequence
+ */
+ public void addDefaultSequence(Class<?> beanClass, Class<?>[] groupSequence) {
+ defaultSequences.put(beanClass, groupSequence);
+ }
+
+ /**
+ * Retrieve the runtime constraint configuration for a given class.
+ *
+ * @param <T>
+ * @param beanClass
+ * @return List of {@link MetaConstraint}s applicable to
+ * <code>beanClass</code>
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public <T> List<MetaConstraint<T, ? extends Annotation>> getMetaConstraints(
+ Class<T> beanClass) {
+ List<MetaConstraint<?, ? extends Annotation>> slot =
+ constraintMap.get(beanClass);
+ if (slot != null) {
+ // noinspection RedundantCast
+ return (List) slot;
+ } else {
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ /**
+ * Get the {@link AccessStrategy} {@link List} indicating nested bean
+ * validations that must be triggered in the course of validating a
+ * <code>beanClass</code> graph.
+ *
+ * @param beanClass
+ * @return {@link List} of {@link AccessStrategy}
+ */
+ public List<AccessStrategy> getValidAccesses(Class<?> beanClass) {
+ List<AccessStrategy> slot = validAccesses.get(beanClass);
+ if (slot != null) {
+ return slot;
+ } else {
+ return Collections.<AccessStrategy>emptyList();
+ }
+ }
+
+ /**
+ * Get the default group sequence configured for <code>beanClass</code>.
+ *
+ * @param beanClass
+ * @return group Class array
+ */
+ public Class<?>[] getDefaultSequence(Class<?> beanClass) {
+ return defaultSequences.get(beanClass);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidation.java
new file mode 100644
index 0000000..8d7dba4
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidation.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Description: unified interface to accumulate {@link ConstraintValidation}s
+ * to varied targets.<br/>
+ */
+public interface AppendValidation {
+ /**
+ * Append a {@link ConstraintValidation}.
+ * @param <T>
+ * @param validation
+ */
+ <T extends Annotation> void append(ConstraintValidation<T> validation);
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java
new file mode 100644
index 0000000..d112251
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.jsr303.xml.AnnotationProxyBuilder;
+
+import javax.validation.Payload;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Description: Adapt {@link AnnotationConstraintBuilder} to the {@link AppendValidation} interface.<br/>
+ */
+public class AppendValidationToBuilder extends BaseAppendValidation {
+ private final AnnotationConstraintBuilder<?> builder;
+
+ /**
+ * Create a new AppendValidationToBuilder instance.
+ * @param builder
+ */
+ public AppendValidationToBuilder(AnnotationConstraintBuilder<?> builder) {
+ this.builder = builder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T extends Annotation> void preProcessValidation(ConstraintValidation<T> validation) {
+ // JSR-303 2.3:
+ // Groups from the main constraint annotation are inherited by the composing annotations.
+ // Any groups definition on a composing annotation is ignored.
+ Set<Class<?>> inheritedGroups = builder.getConstraintValidation().getGroups();
+ validation.setGroups(inheritedGroups);
+
+ // JSR-303 2.3 p:
+ // Payloads are also inherited
+ Set<Class<? extends Payload>> inheritedPayload = builder.getConstraintValidation().getPayload();
+ validation.setPayload(inheritedPayload);
+
+ // Inherited groups and payload values must also be replicated in the
+ // annotation, so it has to be substituted with a new proxy.
+ T originalAnnot = validation.getAnnotation();
+ AnnotationProxyBuilder<T> apb = new AnnotationProxyBuilder<T>(originalAnnot);
+ apb.putValue(ConstraintAnnotationAttributes.GROUPS.getAttributeName(),
+ inheritedGroups.toArray(new Class[inheritedGroups.size()]));
+ apb.putValue(ConstraintAnnotationAttributes.PAYLOAD.getAttributeName(),
+ inheritedPayload.toArray(new Class[inheritedPayload.size()]));
+ T newAnnot = apb.createAnnotation();
+ validation.setAnnotation(newAnnot);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+ builder.addComposed(validation);
+ }
+
+ /**
+ * Get inherited groups.
+ * @return The set of groups from the parent constraint.
+ */
+ public Set<Class<?>> getInheritedGroups() {
+ return builder.getConstraintValidation().getGroups();
+ }
+
+ /**
+ * Get inherited payload.
+ * @return The set of payloads from the parent constraint.
+ */
+ public Set<Class<? extends Payload>> getInheritedPayload() {
+ return builder.getConstraintValidation().getPayload();
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToMeta.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToMeta.java
new file mode 100644
index 0000000..e871289
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToMeta.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.model.FeaturesCapable;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Description: adapt any {@link FeaturesCapable} from the core meta-model to the {@link AppendValidation} interface.<br/>
+ */
+public class AppendValidationToMeta extends BaseAppendValidation {
+ private final FeaturesCapable feature;
+
+ /**
+ * Create a new AppendValidationToMeta instance.
+ * @param meta
+ */
+ public AppendValidationToMeta(FeaturesCapable meta) {
+ this.feature = meta;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+ feature.addValidation(validation);
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
new file mode 100644
index 0000000..cb214a3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Abstract base validation appender that initializes the
+ * {@link ConstraintValidation#getValidator()} on post-processing.
+ *
+ * @author Carlos Vara
+ */
+public abstract class BaseAppendValidation implements AppendValidation {
+
+ /**
+ * {@inheritDoc}
+ *
+ * Append operation divided in 3 stages: pre & post processing and the
+ * "real" append process.
+ */
+ public final <T extends Annotation> void append(ConstraintValidation<T> validation) {
+ preProcessValidation(validation);
+ performAppend(validation);
+ postProcessValidation(validation);
+ }
+
+ /**
+ * Performs the actual "appending" operation to the underlying data
+ * structure that holds the validations. Implementations shouldn't try to do
+ * more than that in this step.
+ *
+ * @param <T>
+ * The type of the validation.
+ * @param validation
+ * The validation to be appended.
+ */
+ public abstract <T extends Annotation> void performAppend(ConstraintValidation<T> validation);
+
+ /**
+ * Pre-process the validation before appending it.
+ *
+ * @param <T>
+ * The type of the validation.
+ * @param validation
+ * The validation to be appended.
+ */
+ public <T extends Annotation> void preProcessValidation(ConstraintValidation<T> validation) {
+ // No generic pre-processing
+ }
+
+ /**
+ * Post-process the validation once it has been appended.
+ *
+ * @param <T>
+ * The type of the validation.
+ * @param validation
+ * The validation to be appended.
+ */
+ public <T extends Annotation> void postProcessValidation(ConstraintValidation<T> validation) {
+ // Initialize the validator
+ validation.initialize();
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java
new file mode 100644
index 0000000..1bf500b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: Implements {@link BeanDescriptor}.<br/>
+ */
+public class BeanDescriptorImpl extends ElementDescriptorImpl implements BeanDescriptor {
+ /**
+ * The {@link ApacheFactoryContext} (not) used by this
+ * {@link BeanDescriptorImpl}
+ */
+ protected final ApacheFactoryContext factoryContext;
+
+ /**
+ * Create a new BeanDescriptorImpl instance.
+ *
+ * @param factoryContext
+ * @param metaBean
+ */
+ protected BeanDescriptorImpl(ApacheFactoryContext factoryContext, MetaBean metaBean) {
+ super(metaBean, metaBean.getBeanClass(), metaBean.getValidations());
+ this.factoryContext = factoryContext;
+ }
+
+ /**
+ * Returns true if the bean involves validation:
+ * <ul>
+ * <li>a constraint is hosted on the bean itself</li>
+ * <li>a constraint is hosted on one of the bean properties, OR</li>
+ * <li>a bean property is marked for cascade (<code>@Valid</code>)</li>
+ * </ul>
+ *
+ * @return true if the bean involves validation
+ */
+ public boolean isBeanConstrained() {
+ if (hasAnyConstraints())
+ return true;
+ for (MetaProperty mprop : metaBean.getProperties()) {
+ if (mprop.getMetaBean() != null || mprop.getFeature(Features.Property.REF_CASCADE) != null)
+ return true;
+ }
+ return false;
+ }
+
+ private boolean hasAnyConstraints() {
+ if (hasConstraints())
+ return true;
+ for (MetaProperty mprop : metaBean.getProperties()) {
+ if (getConstraintDescriptors(mprop.getValidations()).size() > 0)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Return the property level constraints for a given propertyName or {@code null} if
+ * either the property does not exist or has no constraint. The returned
+ * object (and associated objects including ConstraintDescriptors) are
+ * immutable.
+ *
+ * @param propertyName
+ * property evaluated
+ */
+ public PropertyDescriptor getConstraintsForProperty(String propertyName) {
+ if (propertyName == null || propertyName.trim().length() == 0) {
+ throw new IllegalArgumentException("propertyName cannot be null or empty");
+ }
+ MetaProperty prop = metaBean.getProperty(propertyName);
+ if (prop == null)
+ return null;
+ // If no constraints and not cascaded, return null
+ if (prop.getValidations().length == 0 && prop.getFeature(Features.Property.REF_CASCADE) == null) {
+ return null;
+ }
+ return getPropertyDescriptor(prop);
+ }
+
+ private PropertyDescriptor getPropertyDescriptor(MetaProperty prop) {
+ PropertyDescriptorImpl edesc = prop.getFeature(Jsr303Features.Property.PropertyDescriptor);
+ if (edesc == null) {
+ edesc = new PropertyDescriptorImpl(prop);
+ prop.putFeature(Jsr303Features.Property.PropertyDescriptor, edesc);
+ }
+ return edesc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return the property descriptors having at least a constraint defined
+ */
+ public Set<PropertyDescriptor> getConstrainedProperties() {
+ Set<PropertyDescriptor> validatedProperties = new HashSet<PropertyDescriptor>();
+ for (MetaProperty prop : metaBean.getProperties()) {
+ if (prop.getValidations().length > 0
+ || (prop.getMetaBean() != null || prop.getFeature(Features.Property.REF_CASCADE) != null)) {
+ validatedProperties.add(getPropertyDescriptor(prop));
+ }
+ }
+ return Collections.unmodifiableSet(validatedProperties);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "BeanDescriptorImpl{" + "returnType=" + elementClass + '}';
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java
new file mode 100644
index 0000000..a830960
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validator;
+
+/**
+ * Per the bean validation spec, {@link Valid} is not honored by the
+ * {@link #validateProperty(Object, String, Class...)} and
+ * {@link #validateValue(Class, String, Object, Class...)} methods. The
+ * {@link CascadingPropertyValidator} interface thus defines a {@link Validator} that
+ * provides corresponding methods that <em>may</em> honor {@link Valid}.
+ * It should be noted that {@link Validator#validateProperty(Object, String, Class...)}
+ * and {@link Validator#validateValue(Class, String, Object, Class...)} are assumed
+ * semantically equivalent to calling the {@link CascadingPropertyValidator}-defined
+ * methods with <code>cascade == false</code>.
+ *
+ * @version $Rev: 993539 $ $Date: 2010-09-07 16:27:50 -0500 (Tue, 07 Sep 2010) $
+ */
+public interface CascadingPropertyValidator extends Validator {
+
+ /**
+ * Validates all constraints placed on <code>object</code>'s
+ * <code>propertyName</code> property, with optional validation cascading.
+ *
+ * @param <T>
+ * @param object
+ * @param propertyName
+ * @param cascade
+ * @param groups
+ * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+ */
+ <T extends Object> java.util.Set<javax.validation.ConstraintViolation<T>> validateProperty(T object,
+ String propertyName, boolean cascade, java.lang.Class<?>... groups);
+
+ /**
+ * Validates all constraints placed on <code>object</code>'s
+ * <code>propertyName</code> property, with optional validation cascading,
+ * given a hypothetical property <code>value</code>.
+ *
+ * @param <T>
+ * @param beanType
+ * @param propertyName
+ * @param value
+ * @param cascade
+ * @param groups
+ * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+ */
+ <T extends Object> java.util.Set<javax.validation.ConstraintViolation<T>> validateValue(
+ java.lang.Class<T> beanType, String propertyName, Object value, boolean cascade, java.lang.Class<?>... groups);
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
new file mode 100644
index 0000000..571c956
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
@@ -0,0 +1,817 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import javax.validation.metadata.BeanDescriptor;
+
+import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.jsr303.util.PathNavigation;
+import org.apache.bval.jsr303.util.ValidationContextTraversal;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.FeaturesCapable;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.ValidationHelper;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+// TODO: centralize treatMapsLikeBeans
+
+/**
+ * Objects of this class are able to validate bean instances (and the associated object graphs).
+ * <p/>
+ * Implementation is thread-safe.
+ * <p/>
+ * API class
+ *
+ * @version $Rev$ $Date$
+ *
+ * @author Roman Stumm
+ * @author Carlos Vara
+ */
+public class ClassValidator implements CascadingPropertyValidator {
+ private static final Object VALIDATE_PROPERTY = new Object() {
+ public String toString() {
+ return "VALIDATE_PROPERTY";
+ }
+ };
+
+ /**
+ * {@link ApacheFactoryContext} used
+ */
+ protected final ApacheFactoryContext factoryContext;
+
+ /**
+ * {@link GroupsComputer} used
+ */
+ protected final GroupsComputer groupsComputer = new GroupsComputer();
+
+ /**
+ * Create a new ClassValidator instance.
+ *
+ * @param factoryContext
+ */
+ public ClassValidator(ApacheFactoryContext factoryContext) {
+ this.factoryContext = factoryContext;
+ }
+
+ /**
+ * Create a new ClassValidator instance.
+ *
+ * @param factory
+ * @deprecated provided for backward compatibility
+ */
+ public ClassValidator(ApacheValidatorFactory factory) {
+ this(factory.usingContext());
+ }
+
+ /**
+ * Get the metabean finder associated with this validator.
+ *
+ * @return a MetaBeanFinder
+ * @see org.apache.bval.MetaBeanManagerFactory#getFinder()
+ */
+ protected MetaBeanFinder getMetaBeanFinder() {
+ return factoryContext.getMetaBeanFinder();
+ }
+
+ // Validator implementation
+ // --------------------------------------------------
+
+ /**
+ * {@inheritDoc} Validates all constraints on <code>object</code>.
+ *
+ * @param object object to validate
+ * @param groups group or list of groups targeted for validation (default to
+ * {@link javax.validation.groups.Default})
+ * @return constraint violations or an empty Set if none
+ * @throws IllegalArgumentException if object is null or if null is passed to the varargs groups
+ * @throws ValidationException if a non recoverable error happens during the validation
+ * process
+ */
+ // @Override - not allowed in 1.5 for Interface methods
+ @SuppressWarnings("unchecked")
+ public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
+ if (object == null)
+ throw new IllegalArgumentException("cannot validate null");
+ checkGroups(groups);
+
+ try {
+
+ Class<T> objectClass = (Class<T>) object.getClass();
+ MetaBean objectMetaBean = getMetaBeanFinder().findForClass(objectClass);
+
+ final GroupValidationContext<T> context = createContext(objectMetaBean, object, objectClass, groups);
+ final ConstraintValidationListener<T> result = context.getListener();
+ final Groups sequence = context.getGroups();
+
+ // 1. process groups
+ for (Group current : sequence.getGroups()) {
+ context.setCurrentGroup(current);
+ validateBeanNet(context);
+ }
+
+ // 2. process sequences
+ for (List<Group> eachSeq : sequence.getSequences()) {
+ for (Group current : eachSeq) {
+ context.setCurrentGroup(current);
+ validateBeanNet(context);
+ // if one of the group process in the sequence leads to one
+ // or more validation failure,
+ // the groups following in the sequence must not be
+ // processed
+ if (!result.isEmpty())
+ break;
+ }
+ if (!result.isEmpty())
+ break;
+ }
+ return result.getConstraintViolations();
+ } catch (RuntimeException ex) {
+ throw unrecoverableValidationError(ex, object);
+ }
+ }
+
+ /**
+ * {@inheritDoc} Validates all constraints placed on the property of <code>object</code> named
+ * <code>propertyName</code>.
+ *
+ * @param object object to validate
+ * @param propertyName property to validate (ie field and getter constraints). Nested
+ * properties may be referenced (e.g. prop[2].subpropA.subpropB)
+ * @param groups group or list of groups targeted for validation (default to
+ * {@link javax.validation.groups.Default})
+ * @return constraint violations or an empty Set if none
+ * @throws IllegalArgumentException if <code>object</code> is null, if <code>propertyName</code>
+ * null, empty or not a valid object property or if null is
+ * passed to the varargs groups
+ * @throws ValidationException if a non recoverable error happens during the validation
+ * process
+ */
+ // @Override - not allowed in 1.5 for Interface methods
+ public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
+ return validateProperty(object, propertyName, false, groups);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade,
+ Class<?>... groups) {
+
+ if (object == null)
+ throw new IllegalArgumentException("cannot validate null");
+
+ @SuppressWarnings("unchecked")
+ Set<ConstraintViolation<T>> result =
+ validateValueImpl((Class<T>) object.getClass(), object, propertyName, VALIDATE_PROPERTY, cascade, groups);
+ return result;
+ }
+
+ /**
+ * {@inheritDoc} Validates all constraints placed on the property named <code>propertyName</code> of the class
+ * <code>beanType</code> would the property value be <code>value</code>
+ * <p/>
+ * <code>ConstraintViolation</code> objects return null for {@link ConstraintViolation#getRootBean()} and
+ * {@link ConstraintViolation#getLeafBean()}
+ *
+ * @param beanType the bean type
+ * @param propertyName property to validate
+ * @param value property value to validate
+ * @param groups group or list of groups targeted for validation (default to
+ * {@link javax.validation.groups.Default})
+ * @return constraint violations or an empty Set if none
+ * @throws IllegalArgumentException if <code>beanType</code> is null, if
+ * <code>propertyName</code> null, empty or not a valid object
+ * property or if null is passed to the varargs groups
+ * @throws ValidationException if a non recoverable error happens during the validation
+ * process
+ */
+ // @Override - not allowed in 1.5 for Interface methods
+ public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+ Class<?>... groups) {
+ return validateValue(beanType, propertyName, value, false, groups);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+ boolean cascade, Class<?>... groups) {
+ return validateValueImpl(checkBeanType(beanType), null, propertyName, value, cascade, groups);
+ }
+
+ /**
+ * {@inheritDoc} Return the descriptor object describing bean constraints. The returned object (and associated
+ * objects including <code>ConstraintDescriptor<code>s) are immutable.
+ *
+ * @param clazz class or interface type evaluated
+ * @return the bean descriptor for the specified class.
+ * @throws IllegalArgumentException if clazz is null
+ * @throws ValidationException if a non recoverable error happens during the metadata
+ * discovery or if some constraints are invalid.
+ */
+ // @Override - not allowed in 1.5 for Interface methods
+ public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
+ if (clazz == null) {
+ throw new IllegalArgumentException("Class cannot be null");
+ }
+ try {
+ MetaBean metaBean = getMetaBeanFinder().findForClass(clazz);
+ BeanDescriptorImpl edesc = metaBean.getFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR);
+ if (edesc == null) {
+ edesc = createBeanDescriptor(metaBean);
+ metaBean.putFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR, edesc);
+ }
+ return edesc;
+ } catch (RuntimeException ex) {
+ throw new ValidationException("error retrieving constraints for " + clazz, ex);
+ }
+ }
+
+ /**
+ * {@inheritDoc} Return an instance of the specified type allowing access to provider-specific APIs. If the Bean
+ * Validation provider implementation does not support the specified class, <code>ValidationException</code> is
+ * thrown.
+ *
+ * @param type the class of the object to be returned.
+ * @return an instance of the specified class
+ * @throws ValidationException if the provider does not support the call.
+ */
+ // @Override - not allowed in 1.5 for Interface methods
+ public <T> T unwrap(Class<T> type) {
+ // FIXME 2011-03-27 jw:
+ // This code is unsecure.
+ // It should allow only a fixed set of classes.
+ // Can't fix this because don't know which classes this method should support.
+
+ if (type.isAssignableFrom(getClass())) {
+ @SuppressWarnings("unchecked")
+ final T result = (T) this;
+ return result;
+ } else if (!(type.isInterface() || Modifier.isAbstract(type.getModifiers()))) {
+ return newInstance(type);
+ } else {
+ try {
+ Class<?> cls = ClassUtils.getClass(type.getName() + "Impl");
+ if (type.isAssignableFrom(cls)) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends T> implClass = (Class<? extends T>) cls;
+ return newInstance(implClass);
+ }
+ } catch (ClassNotFoundException e) {
+ }
+ throw new ValidationException("Type " + type + " not supported");
+ }
+ }
+
+ private <T> T newInstance(final Class<T> cls) {
+ return AccessController.doPrivileged(new PrivilegedAction<T>() {
+ public T run() {
+ try {
+ Constructor<T> cons = cls.getConstructor(ApacheFactoryContext.class);
+ if (!cons.isAccessible()) {
+ cons.setAccessible(true);
+ }
+ return cons.newInstance(factoryContext);
+ } catch (final Exception ex) {
+ throw new ValidationException("Cannot instantiate : " + cls, ex);
+ }
+ }
+ });
+ }
+
+ // Helpers
+ // -------------------------------------------------------------------
+
+ /**
+ * Validates a bean and all its cascaded related beans for the currently defined group.
+ * <p/>
+ * Special code is present to manage the {@link Default} group.
+ *
+ * @param validationContext The current context of this validation call. Must have its
+ * {@link GroupValidationContext#getCurrentGroup()} field set.
+ */
+ protected void validateBeanNet(GroupValidationContext<?> context) {
+
+ // If reached a cascaded bean which is null
+ if (context.getBean() == null) {
+ return;
+ }
+
+ // If reached a cascaded bean which has already been validated for the
+ // current group
+ if (!context.collectValidated()) {
+ return;
+ }
+
+ // ### First, validate the bean
+
+ // Default is a special case
+ if (context.getCurrentGroup().isDefault()) {
+
+ List<Group> defaultGroups = expandDefaultGroup(context);
+ final ConstraintValidationListener<?> result = (ConstraintValidationListener<?>) context.getListener();
+
+ // If the rootBean defines a GroupSequence
+ if (defaultGroups != null && defaultGroups.size() > 1) {
+
+ int numViolations = result.violationsSize();
+
+ // Validate the bean for each group in the sequence
+ Group currentGroup = context.getCurrentGroup();
+ for (Group each : defaultGroups) {
+ context.setCurrentGroup(each);
+ ValidationHelper.validateBean(context);
+ // Spec 3.4.3 - Stop validation if errors already found
+ if (result.violationsSize() > numViolations) {
+ break;
+ }
+ }
+ context.setCurrentGroup(currentGroup);
+ } else {
+
+ // For each class in the hierarchy of classes of rootBean,
+ // validate the constraints defined in that class according
+ // to the GroupSequence defined in the same class
+
+ // Obtain the full class hierarchy
+ List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
+ ClassHelper.fillFullClassHierarchyAsList(classHierarchy, context.getMetaBean().getBeanClass());
+ Class<?> initialOwner = context.getCurrentOwner();
+
+ // For each owner in the hierarchy
+ for (Class<?> owner : classHierarchy) {
+ context.setCurrentOwner(owner);
+
+ int numViolations = result.violationsSize();
+
+ // Obtain the group sequence of the owner, and use it for
+ // the constraints that belong to it
+ List<Group> ownerDefaultGroups =
+ context.getMetaBean().getFeature("{GroupSequence:" + owner.getCanonicalName() + "}");
+ for (Group each : ownerDefaultGroups) {
+ context.setCurrentGroup(each);
+ ValidationHelper.validateBean(context);
+ // Spec 3.4.3 - Stop validation if errors already found
+ if (result.violationsSize() > numViolations) {
+ break;
+ }
+ }
+
+ }
+ context.setCurrentOwner(initialOwner);
+ context.setCurrentGroup(Group.DEFAULT);
+
+ }
+
+ }
+ // if not the default group, proceed as normal
+ else {
+ ValidationHelper.validateBean(context);
+ }
+
+ // ### Then, the cascaded beans (@Valid)
+ for (MetaProperty prop : context.getMetaBean().getProperties()) {
+ validateCascadedBean(context, prop);
+ }
+
+ }
+
+ /**
+ * Checks if the the meta property <code>prop</code> defines a cascaded bean, and in case it does, validates it.
+ *
+ * @param context The current validation context.
+ * @param prop The property to cascade from (in case it is possible).
+ */
+ private void validateCascadedBean(GroupValidationContext<?> context, MetaProperty prop) {
+ AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
+ if (access != null) { // different accesses to relation
+ // save old values from context
+ final Object bean = context.getBean();
+ final MetaBean mbean = context.getMetaBean();
+ // TODO implement Validation.groups support on related bean
+// Class[] groups = prop.getFeature(Jsr303Features.Property.REF_GROUPS);
+ for (AccessStrategy each : access) {
+ if (isCascadable(context, prop, each)) {
+ // modify context state for relationship-target bean
+ context.moveDown(prop, each);
+ // Now, if the related bean is an instance of Map/Array/etc,
+ ValidationHelper
+ .validateContext(context, new Jsr303ValidationCallback(context), treatMapsLikeBeans);
+ // restore old values in context
+ context.moveUp(bean, mbean);
+ }
+ }
+ }
+ }
+
+ /**
+ * Before accessing a related bean (marked with {@link javax.validation.Valid}), the validator has to check if it is
+ * reachable and cascadable.
+ *
+ * @param context The current validation context.
+ * @param prop The property of the related bean.
+ * @param access The access strategy used to get the related bean value.
+ * @return <code>true</code> if the validator can access the related bean, <code>false</code> otherwise.
+ */
+ private boolean isCascadable(GroupValidationContext<?> context, MetaProperty prop, AccessStrategy access) {
+
+ PathImpl beanPath = context.getPropertyPath();
+ NodeImpl node = new NodeImpl(prop.getName());
+ if (beanPath == null) {
+ beanPath = PathImpl.create(null);
+ }
+ try {
+ if (!context.getTraversableResolver().isReachable(context.getBean(), node,
+ context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+ return false;
+ } catch (RuntimeException e) {
+ throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e);
+ }
+
+ try {
+ if (!context.getTraversableResolver().isCascadable(context.getBean(), node,
+ context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+ return false;
+ } catch (RuntimeException e) {
+ throw new ValidationException("Error TraversableResolver.isCascadable() for " + context.getBean(), e);
+ }
+
+ return true;
+ }
+
+ /**
+ * in case of a default group return the list of groups for a redefined default GroupSequence
+ *
+ * @return null when no in default group or default group sequence not redefined
+ */
+ private List<Group> expandDefaultGroup(GroupValidationContext<?> context) {
+ if (context.getCurrentGroup().isDefault()) {
+ // mention if metaBean redefines the default group
+ List<Group> groupSeq = context.getMetaBean().getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ if (groupSeq != null) {
+ context.getGroups().assertDefaultGroupSequenceIsExpandable(groupSeq);
+ }
+ return groupSeq;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Generate an unrecoverable validation error
+ *
+ * @param ex
+ * @param object
+ * @return a {@link RuntimeException} of the appropriate type
+ */
+ @SuppressWarnings("finally")
+ protected static RuntimeException unrecoverableValidationError(RuntimeException ex, Object object) {
+ if (ex instanceof UnknownPropertyException || ex instanceof IncompatiblePropertyValueException) {
+ // Convert to IllegalArgumentException
+ return new IllegalArgumentException(ex.getMessage(), ex);
+ } else if (ex instanceof ValidationException) {
+ return ex; // do not wrap specific ValidationExceptions (or
+ // instances from subclasses)
+ } else {
+ String objectId = "";
+ try {
+ if (object != null) {
+ objectId = object.toString();
+ } else {
+ objectId = "<null>";
+ }
+ } catch (Exception e) {
+ objectId = "<unknown>";
+ } finally {
+ return new ValidationException("error during validation of "
+ + objectId, ex);
+ }
+ }
+ }
+
+ private void validatePropertyInGroup(final GroupValidationContext<?> context) {
+ final Runnable helper;
+ if (context.getMetaProperty() == null) {
+ helper = new Runnable() {
+
+ public void run() {
+ ValidationHelper.validateBean(context);
+ }
+ };
+ } else {
+ helper = new Runnable() {
+
+ public void run() {
+ ValidationHelper.validateProperty(context);
+ }
+ };
+ }
+ Group currentGroup = context.getCurrentGroup();
+ List<Group> defaultGroups = expandDefaultGroup(context);
+ if (defaultGroups != null) {
+ for (Group each : defaultGroups) {
+ context.setCurrentGroup(each);
+ helper.run();
+ // continue validation, even if errors already found
+ }
+ context.setCurrentGroup(currentGroup); // restore
+ } else {
+ helper.run();
+ }
+ }
+
+ /**
+ * Create a {@link GroupValidationContext}.
+ *
+ * @param <T>
+ * @param metaBean
+ * @param object
+ * @param objectClass
+ * @param groups
+ * @return {@link GroupValidationContext} instance
+ */
+ protected <T> GroupValidationContext<T> createContext(MetaBean metaBean, T object, Class<T> objectClass,
+ Class<?>... groups) {
+ ConstraintValidationListener<T> listener = new ConstraintValidationListener<T>(object, objectClass);
+ GroupValidationContextImpl<T> context =
+ new GroupValidationContextImpl<T>(listener, this.factoryContext.getMessageInterpolator(),
+ this.factoryContext.getTraversableResolver(), metaBean);
+ context.setBean(object, metaBean);
+ context.setGroups(groupsComputer.computeGroups(groups));
+ return context;
+ }
+
+ /**
+ * Create a {@link BeanDescriptorImpl}
+ *
+ * @param metaBean
+ * @return {@link BeanDescriptorImpl} instance
+ */
+ protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
+ return new BeanDescriptorImpl(factoryContext, metaBean);
+ }
+
+ private boolean treatMapsLikeBeans = false;
+
+ /**
+ * Behavior configuration -
+ * <p/>
+ * <pre>
+ * @return treatMapsLikeBeans - true (validate maps like beans, so that
+ * you can use Maps to validate dynamic classes or
+ * beans for which you have the MetaBean but no instances)
+ * - false (default), validate maps like collections
+ * (validating the values only)
+ * </pre>
+ * <p/>
+ * (is still configuration to better in BeanValidationContext?)
+ */
+ public boolean isTreatMapsLikeBeans() {
+ return treatMapsLikeBeans;
+ }
+
+ /**
+ * Set whether maps are to be treated like beans.
+ *
+ * <pre>
+ * @param treatMapsLikeBeans - true (validate maps like beans, so that
+ * you can use Maps to validate dynamic classes or
+ * beans for which you have the MetaBean but no instances)
+ * - false (default), validate maps like collections
+ * (validating the values only)
+ * </pre>
+ */
+ public void setTreatMapsLikeBeans(boolean treatMapsLikeBeans) {
+ this.treatMapsLikeBeans = treatMapsLikeBeans;
+ }
+
+ /**
+ * Checks that beanType is valid according to spec Section 4.1.1 i. Throws an {@link IllegalArgumentException} if it
+ * is not.
+ *
+ * @param beanType Bean type to check.
+ */
+ private <T> Class<T> checkBeanType(Class<T> beanType) {
+ if (beanType == null) {
+ throw new IllegalArgumentException("Bean type cannot be null.");
+ }
+ return beanType;
+ }
+
+ /**
+ * Checks that the property name is valid according to spec Section 4.1.1 i. Throws an
+ * {@link IllegalArgumentException} if it is not.
+ *
+ * @param propertyName Property name to check.
+ */
+ private void checkPropertyName(String propertyName) {
+ if (propertyName == null || propertyName.trim().length() == 0) {
+ throw new IllegalArgumentException("Property path cannot be null or empty.");
+ }
+ }
+
+ /**
+ * Checks that the groups array is valid according to spec Section 4.1.1 i. Throws an
+ * {@link IllegalArgumentException} if it is not.
+ *
+ * @param groups The groups to check.
+ */
+ private void checkGroups(Class<?>[] groups) {
+ if (groups == null) {
+ throw new IllegalArgumentException("Groups cannot be null.");
+ }
+ }
+
+ /**
+ * Dispatches a call from {@link #validate()} to {@link ClassValidator#validateBeanNet(GroupValidationContext)} with
+ * the current context set.
+ */
+ protected class Jsr303ValidationCallback implements ValidationHelper.ValidateCallback {
+
+ private final GroupValidationContext<?> context;
+
+ public Jsr303ValidationCallback(GroupValidationContext<?> context) {
+ this.context = context;
+ }
+
+ public void validate() {
+ validateBeanNet(context);
+ }
+
+ }
+
+ /**
+ * Create a {@link ValidationContextTraversal} instance for this {@link ClassValidator}.
+ *
+ * @param validationContext
+ * @return {@link ValidationContextTraversal}
+ */
+ protected ValidationContextTraversal createValidationContextTraversal(GroupValidationContext<?> validationContext) {
+ return new ValidationContextTraversal(validationContext);
+ }
+
+ /**
+ * Implement {@link #validateProperty(Object, String, boolean, Class...)} and
+ * {@link #validateValue(Class, String, Object, boolean, Class...)}.
+ *
+ * @param <T>
+ * @param beanType
+ * @param object
+ * @param propertyName
+ * @param value
+ * @param cascade
+ * @param groups
+ * @return {@link ConstraintViolation} {@link Set}
+ */
+ private <T> Set<ConstraintViolation<T>> validateValueImpl(Class<T> beanType, T object, String propertyName,
+ Object value, final boolean cascade, Class<?>... groups) {
+
+ assert (object == null) ^ (value == VALIDATE_PROPERTY);
+ checkPropertyName(propertyName);
+ checkGroups(groups);
+
+ try {
+ final MetaBean initialMetaBean = new DynamicMetaBean(getMetaBeanFinder());
+ initialMetaBean.setBeanClass(beanType);
+ GroupValidationContext<T> context = createContext(initialMetaBean, object, beanType, groups);
+ ValidationContextTraversal contextTraversal = createValidationContextTraversal(context);
+ PathNavigation.navigate(propertyName, contextTraversal);
+
+ MetaProperty prop = context.getMetaProperty();
+ boolean fixed = false;
+ if (value != VALIDATE_PROPERTY) {
+ assert !context.getPropertyPath().isRootPath();
+ if (prop == null && value != null) {
+ context.setMetaBean(getMetaBeanFinder().findForClass(value.getClass()));
+ }
+ if (!cascade) {
+ //TCK doesn't care what type a property is if there are no constraints to validate:
+ FeaturesCapable meta = prop == null ? context.getMetaBean() : prop;
+ if (ArrayUtils.isEmpty(meta.getValidations())) {
+ return Collections.<ConstraintViolation<T>> emptySet();
+ }
+ }
+ if (!TypeUtils.isAssignable(value == null ? null : value.getClass(), contextTraversal.getType())) {
+ throw new IncompatiblePropertyValueException(String.format(
+ "%3$s is not a valid value for property %2$s of type %1$s", beanType, propertyName, value));
+ }
+ if (prop == null) {
+ context.setBean(value);
+ } else {
+ context.setFixedValue(value);
+ fixed = true;
+ }
+ }
+ boolean doCascade = cascade && (prop == null || prop.getMetaBean() != null);
+
+ Object bean = context.getBean();
+
+ ConstraintValidationListener<T> result = context.getListener();
+ Groups sequence = context.getGroups();
+
+ // 1. process groups
+
+ for (Group current : sequence.getGroups()) {
+ context.setCurrentGroup(current);
+
+ if (!doCascade || prop != null) {
+ validatePropertyInGroup(context);
+ }
+ if (doCascade) {
+ contextTraversal.moveDownIfNecessary();
+ if (context.getMetaBean() instanceof DynamicMetaBean) {
+ context.setMetaBean(context.getMetaBean().resolveMetaBean(
+ ObjectUtils.defaultIfNull(context.getBean(), contextTraversal.getRawType())));
+ }
+ validateBeanNet(context);
+ if (prop != null) {
+ context.moveUp(bean, prop.getParentMetaBean());
+ context.setMetaProperty(prop);
+ if (fixed) {
+ context.setFixedValue(value);
+ }
+ }
+ }
+ }
+
+ // 2. process sequences
+
+ int groupViolations = result.getConstraintViolations().size();
+
+ outer: for (List<Group> eachSeq : sequence.getSequences()) {
+ for (Group current : eachSeq) {
+ context.setCurrentGroup(current);
+
+ if (!doCascade || prop != null) {
+ validatePropertyInGroup(context);
+ }
+ if (doCascade) {
+ contextTraversal.moveDownIfNecessary();
+ if (context.getMetaBean() instanceof DynamicMetaBean) {
+ context.setMetaBean(context.getMetaBean().resolveMetaBean(
+ ObjectUtils.defaultIfNull(context.getBean(), contextTraversal.getRawType())));
+ }
+ validateBeanNet(context);
+ if (prop != null) {
+ context.moveUp(bean, prop.getParentMetaBean());
+ context.setMetaProperty(prop);
+ if (fixed) {
+ context.setFixedValue(value);
+ }
+ }
+ }
+ /**
+ * if one of the group process in the sequence leads to one or more validation failure, the groups
+ * following in the sequence must not be processed
+ */
+ if (result.getConstraintViolations().size() > groupViolations)
+ break outer;
+ }
+ }
+ return result.getConstraintViolations();
+ } catch (RuntimeException ex) {
+ throw unrecoverableValidationError(ex, ObjectUtils.defaultIfNull(object, value));
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java
new file mode 100644
index 0000000..27a1c42
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+
+import org.apache.bval.jsr303.resolver.DefaultTraversableResolver;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.jsr303.xml.ValidationParser;
+
+import javax.validation.*;
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.logging.Logger;
+
+/**
+ * Description: used to configure apache-validation for jsr303.
+ * Implementation of Configuration that also implements ConfigurationState,
+ * hence this can be passed to buildValidatorFactory(ConfigurationState).
+ * <br/>
+ */
+public class ConfigurationImpl implements ApacheValidatorConfiguration, ConfigurationState {
+ private static final Logger log = Logger.getLogger(ConfigurationImpl.class.getName());
+
+ /**
+ * Configured {@link ValidationProvider}
+ */
+ //couldn't this be parameterized <ApacheValidatorConfiguration> or <? super ApacheValidatorConfiguration>?
+ protected final ValidationProvider<?> provider;
+
+ /**
+ * Configured {@link ValidationProviderResolver}
+ */
+ protected final ValidationProviderResolver providerResolver;
+
+ /**
+ * Configured {@link ValidationProvider} class
+ */
+ protected Class<? extends ValidationProvider<?>> providerClass;
+
+ /**
+ * Configured {@link MessageInterpolator}
+ */
+ protected MessageInterpolator messageInterpolator;
+
+ /**
+ * Configured {@link ConstraintValidatorFactory}
+ */
+ protected ConstraintValidatorFactory constraintValidatorFactory;
+
+ private TraversableResolver traversableResolver;
+
+ // BEGIN DEFAULTS
+ /**
+ * false = dirty flag (to prevent from multiple parsing validation.xml)
+ */
+ private boolean prepared = false;
+ private final TraversableResolver defaultTraversableResolver =
+ new DefaultTraversableResolver();
+
+ /**
+ * Default {@link MessageInterpolator}
+ */
+ protected final MessageInterpolator defaultMessageInterpolator =
+ new DefaultMessageInterpolator();
+
+ private final ConstraintValidatorFactory defaultConstraintValidatorFactory =
+ new DefaultConstraintValidatorFactory();
+ // END DEFAULTS
+
+ private Set<InputStream> mappingStreams = new HashSet<InputStream>();
+ private Map<String, String> properties = new HashMap<String, String>();
+ private boolean ignoreXmlConfiguration = false;
+
+ /**
+ * Create a new ConfigurationImpl instance.
+ * @param aState
+ * @param aProvider
+ */
+ public ConfigurationImpl(BootstrapState aState, ValidationProvider<?> aProvider) {
+ if (aProvider != null) {
+ this.provider = aProvider;
+ this.providerResolver = null;
+ } else if (aState != null) {
+ this.provider = null;
+ if (aState.getValidationProviderResolver() == null) {
+ providerResolver = aState.getDefaultValidationProviderResolver();
+ } else {
+ providerResolver = aState.getValidationProviderResolver();
+ }
+ } else {
+ throw new ValidationException("either provider or state are required");
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ApacheValidatorConfiguration traversableResolver(TraversableResolver resolver) {
+ traversableResolver = resolver;
+ this.prepared = false;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Ignore data from the <i>META-INF/validation.xml</i> file if this
+ * method is called.
+ *
+ * @return this
+ */
+ public ApacheValidatorConfiguration ignoreXmlConfiguration() {
+ ignoreXmlConfiguration = true;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigurationImpl messageInterpolator(MessageInterpolator resolver) {
+ this.messageInterpolator = resolver;
+ this.prepared = false;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConfigurationImpl constraintValidatorFactory(
+ ConstraintValidatorFactory constraintFactory) {
+ this.constraintValidatorFactory = constraintFactory;
+ this.prepared = false;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Add a stream describing constraint mapping in the Bean Validation
+ * XML format.
+ *
+ * @return this
+ */
+ public ApacheValidatorConfiguration addMapping(InputStream stream) {
+ mappingStreams.add(stream);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Add a provider specific property. This property is equivalent to
+ * XML configuration properties.
+ * If we do not know how to handle the property, we silently ignore it.
+ *
+ * @return this
+ */
+ public ApacheValidatorConfiguration addProperty(String name, String value) {
+ properties.put(name, value);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Return a map of non type-safe custom properties.
+ *
+ * @return null
+ */
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Returns true if Configuration.ignoreXMLConfiguration() has been called.
+ * In this case, we ignore META-INF/validation.xml
+ *
+ * @return true
+ */
+ public boolean isIgnoreXmlConfiguration() {
+ return ignoreXmlConfiguration;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<InputStream> getMappingStreams() {
+ return mappingStreams;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MessageInterpolator getMessageInterpolator() {
+ return messageInterpolator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MessageInterpolator getDefaultMessageInterpolator() {
+ return defaultMessageInterpolator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TraversableResolver getDefaultTraversableResolver() {
+ return defaultTraversableResolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() {
+ return defaultConstraintValidatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ * main factory method to build a ValidatorFactory
+ *
+ * @throws ValidationException if the ValidatorFactory cannot be built
+ */
+ public ValidatorFactory buildValidatorFactory() {
+ return run(SecureActions.doPrivBuildValidatorFactory(this));
+ }
+
+ public ValidatorFactory doPrivBuildValidatorFactory() {
+ prepare();
+ if (provider != null) {
+ return provider.buildValidatorFactory(this);
+ } else {
+ return findProvider().buildValidatorFactory(this);
+ }
+ }
+
+ private void prepare() {
+ if (prepared) return;
+ parseValidationXml();
+ applyDefaults();
+ prepared = true;
+ }
+
+ /** Check whether a validation.xml file exists and parses it with JAXB */
+ private void parseValidationXml() {
+ if (isIgnoreXmlConfiguration()) {
+ log.info("ignoreXmlConfiguration == true");
+ } else {
+ new ValidationParser(getProperties().get(Properties.VALIDATION_XML_PATH))
+ .processValidationConfig(this);
+ }
+ }
+
+ private void applyDefaults() {
+ // make sure we use the defaults in case they haven't been provided yet
+ if (traversableResolver == null) {
+ traversableResolver = getDefaultTraversableResolver();
+ }
+ if (messageInterpolator == null) {
+ messageInterpolator = getDefaultMessageInterpolator();
+ }
+ if (constraintValidatorFactory == null) {
+ constraintValidatorFactory = getDefaultConstraintValidatorFactory();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return the constraint validator factory of this configuration.
+ */
+ public ConstraintValidatorFactory getConstraintValidatorFactory() {
+ return constraintValidatorFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TraversableResolver getTraversableResolver() {
+ return traversableResolver;
+ }
+
+ /**
+ * Get the configured {@link ValidationProvider}.
+ * @return {@link ValidationProvider}
+ */
+ public ValidationProvider<?> getProvider() {
+ return provider;
+ }
+
+ private ValidationProvider<?> findProvider() {
+ if (providerClass != null) {
+ for (ValidationProvider<?> provider : providerResolver
+ .getValidationProviders()) {
+ if (providerClass.isAssignableFrom(provider.getClass())) {
+ return provider;
+ }
+ }
+ throw new ValidationException(
+ "Unable to find suitable provider: " + providerClass);
+ } else {
+ List<ValidationProvider<?>> providers = providerResolver.getValidationProviders();
+ return providers.get(0);
+ }
+ }
+
+ /**
+ * Set {@link ValidationProvider} class.
+ * @param providerClass
+ */
+ public void setProviderClass(Class<? extends ValidationProvider<?>> providerClass) {
+ this.providerClass = providerClass;
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
new file mode 100644
index 0000000..972e64f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
@@ -0,0 +1,268 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * Defines the well-known attributes of {@link Constraint} annotations.
+ *
+ * @version $Rev: 1165923 $ $Date: 2011-09-06 18:07:53 -0500 (Tue, 06 Sep 2011) $
+ */
+public enum ConstraintAnnotationAttributes {
+ /**
+ * "message"
+ */
+ MESSAGE,
+
+ /**
+ * "groups"
+ */
+ GROUPS,
+
+ /**
+ * "payload"
+ */
+ PAYLOAD,
+
+ /**
+ * "value" for multi-valued constraints
+ */
+ VALUE(true);
+
+ @SuppressWarnings("unused")
+ private static class Types {
+ String message;
+ Class<?>[] groups;
+ Class<? extends Payload>[] payload;
+ Annotation[] value;
+ }
+
+ private Type type;
+ private boolean permitNullDefaultValue;
+
+ private ConstraintAnnotationAttributes() {
+ this(false);
+ }
+
+ private ConstraintAnnotationAttributes(boolean permitNullDefaultValue) {
+ this.permitNullDefaultValue = permitNullDefaultValue;
+ try {
+ this.type = Types.class.getDeclaredField(getAttributeName()).getGenericType();
+ } catch (Exception e) {
+ // should never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Get the expected type of the represented attribute.
+ *
+ * @return Class<?>
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Get the attribute name represented.
+ *
+ * @return String
+ */
+ public String getAttributeName() {
+ return name().toLowerCase(Locale.US);
+ }
+
+ /**
+ * Put <code>value</code> into a map with <code>this.attributeName</code> as
+ * key.
+ *
+ * @param <V>
+ * @param map
+ * @param value
+ * @return previous value mapped to <code>this.attributeName</code>
+ */
+ public <V> Object put(Map<? super String, ? super V> map, V value) {
+ if (!TypeUtils.isInstance(value, getType())) {
+ throw new IllegalArgumentException(String.format("Invalid '%s' value: %s", getAttributeName(), value));
+ }
+ return map.put(getAttributeName(), value);
+ }
+
+ /**
+ * Get the value of <code>this.attributeName</code> from <code>map</code>.
+ *
+ * @param <V>
+ * @param map
+ * @return V if you say so
+ */
+ public <V> V get(Map<? super String, ? super V> map) {
+ @SuppressWarnings("unchecked")
+ final V result = (V) map.get(getAttributeName());
+ if (!TypeUtils.isInstance(result, getType())) {
+ throw new IllegalStateException(String.format("Invalid '%s' value: %s", getAttributeName(), result));
+ }
+ return result;
+ }
+
+ /**
+ * Verify that this attribute is validly defined on the given type.
+ *
+ * @param type
+ * @throws ConstraintDefinitionException
+ */
+ public <A extends Annotation> void validateOn(Class<A> type) {
+ new Worker<A>(type);
+ }
+
+ /**
+ * Benign means of checking for an attribute's existence.
+ *
+ * @param type
+ * @return whether the attribute was (properly) declared
+ */
+ public <A extends Annotation> boolean isDeclaredOn(Class<A> type) {
+ return new Worker<A>(type, true).valid;
+ }
+
+ /**
+ * Get the value of this attribute from the specified constraint annotation.
+ *
+ * @param constraint
+ * @return Object
+ */
+ public <T> T getValue(Annotation constraint) {
+ try {
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ T result = (T) new Worker(constraint.annotationType()).read(constraint);
+ return result;
+ } catch (Exception e) {
+ throw new ValidationException(String.format("Could not get value of %1$s() from %2$s", getType(),
+ constraint));
+ }
+ }
+
+ /**
+ * Get the default value of this attribute on the given annotation type.
+ * @param <T>
+ * @param type
+ * @return Object
+ */
+ public <T, A extends Annotation> T getDefaultValue(Class<A> type) {
+ @SuppressWarnings("unchecked")
+ final T result = (T) new Worker<A>(type).defaultValue;
+ return result;
+ }
+
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+ private class Worker<C> {
+ final Method method;
+ final Object defaultValue;
+ final boolean valid;
+
+ /**
+ * Create a new Worker instance.
+ * @param constraintType to handle
+ */
+ Worker(Class<C> constraintType) {
+ this(constraintType, false);
+ }
+
+ /**
+ * Create a new Worker instance.
+ * @param constraintType to handle
+ * @param quiet whether to simply set !valid rather than throw an Exception on error
+ */
+ Worker(Class<C> constraintType, boolean quiet) {
+ super();
+ boolean _valid = true;
+ Object _defaultValue = null;
+ try {
+ method = doPrivileged(SecureActions.getPublicMethod(constraintType, getAttributeName()));
+ if (method == null) {
+ if (quiet) {
+ _valid = false;
+ return;
+ }
+ throw new ConstraintDefinitionException(String.format("Annotation %1$s has no %2$s() method",
+ constraintType, getAttributeName()));
+ }
+
+ if (!TypeUtils.isAssignable(method.getReturnType(), getType())) {
+ if (quiet) {
+ _valid = false;
+ return;
+ }
+ throw new ConstraintDefinitionException(String.format(
+ "Return type for %1$s() must be of type %2$s", getAttributeName(), getType()));
+ }
+ _defaultValue = method.getDefaultValue();
+ if (_defaultValue == null && permitNullDefaultValue) {
+ return;
+ }
+ if (TypeUtils.isArrayType(getType()) && Array.getLength(_defaultValue) > 0) {
+ if (quiet) {
+ _valid = false;
+ return;
+ }
+ throw new ConstraintDefinitionException(String.format(
+ "Default value for %1$s() must be an empty array", getAttributeName()));
+ }
+ } finally {
+ valid = _valid;
+ defaultValue = _defaultValue;
+ }
+ }
+
+ <T> T read(final C constraint) {
+ @SuppressWarnings("unchecked")
+ T result = (T) doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ try {
+ method.setAccessible(true);
+ return method.invoke(constraint);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ return result;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintCached.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintCached.java
new file mode 100644
index 0000000..b144d3e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintCached.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description: hold the relationship annotation->validatedBy[] ConstraintValidator classes
+ * that are already parsed in a cache.<br/>
+ */
+public class ConstraintCached {
+ private final Map<Class<? extends Annotation>, Class<? extends ConstraintValidator<?, ?>>[]> classes =
+ new HashMap<Class<? extends Annotation>, Class<? extends ConstraintValidator<?,?>>[]>();
+
+ /**
+ * Record the set of validator classes for a given constraint annotation.
+ * @param annotationClass
+ * @param definitionClasses
+ */
+ public <A extends Annotation> void putConstraintValidator(Class<A> annotationClass,
+ Class<? extends ConstraintValidator<A, ?>>[] definitionClasses) {
+ classes.put(annotationClass, definitionClasses);
+ }
+
+ /**
+ * Learn whether we have cached the validator classes for the requested constraint annotation.
+ * @param annotationClass to look up
+ * @return boolean
+ */
+ public boolean containsConstraintValidator(Class<? extends Annotation> annotationClass) {
+ return classes.containsKey(annotationClass);
+ }
+
+ /**
+ * Get the cached validator classes for the requested constraint annotation.
+ * @param annotationClass to look up
+ * @return array of {@link ConstraintValidator} implementation types
+ */
+ @SuppressWarnings("unchecked")
+ public <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] getConstraintValidators(
+ Class<A> annotationClass) {
+ return (Class<? extends ConstraintValidator<A, ?>>[]) classes.get(annotationClass);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDefaults.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDefaults.java
new file mode 100644
index 0000000..3eb01f5
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDefaults.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: Provides access to the default constraints/validator implementation classes built into the framework.
+ * These are configured in DefaultConstraints.properties.<br/>
+ */
+public class ConstraintDefaults {
+ private static final Logger log = Logger.getLogger(ConstraintDefaults.class.getName());
+ private static final String DEFAULT_CONSTRAINTS =
+ "org/apache/bval/jsr303/DefaultConstraints.properties";
+
+ /**
+ * The default constraint data stored herein.
+ */
+ protected Map<String, Class<? extends ConstraintValidator<?, ?>>[]> defaultConstraints;
+
+ /**
+ * Create a new ConstraintDefaults instance.
+ */
+ public ConstraintDefaults() {
+ defaultConstraints = loadDefaultConstraints(DEFAULT_CONSTRAINTS);
+ }
+
+ /**
+ * Get the default constraint data.
+ * @return String-keyed map
+ */
+ public Map<String, Class<? extends ConstraintValidator<?, ?>>[]> getDefaultConstraints() {
+ return defaultConstraints;
+ }
+
+ /**
+ * Get the default validator implementation types for the specified constraint annotation type.
+ * @param annotationType
+ * @return array of {@link ConstraintValidator} implementation classes
+ */
+ @SuppressWarnings("unchecked")
+ public <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] getValidatorClasses(
+ Class<A> annotationType) {
+ return (Class<? extends ConstraintValidator<A, ?>>[]) getDefaultConstraints().get(annotationType.getName());
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Class<? extends ConstraintValidator<?, ?>>[]> loadDefaultConstraints(String resource) {
+ Properties constraintProperties = new Properties();
+ final ClassLoader classloader = getClassLoader();
+ InputStream stream = classloader.getResourceAsStream(resource);
+ if (stream != null) {
+ try {
+ constraintProperties.load(stream);
+ } catch (IOException e) {
+ log.log(Level.SEVERE, String.format("Cannot load %s", resource), e);
+ }
+ } else {
+ log.log(Level.WARNING, String.format("Cannot find %s", resource));
+ }
+
+ Map<String, Class<? extends ConstraintValidator<?, ?>>[]> loadedConstraints
+ = new HashMap<String, Class<? extends ConstraintValidator<?,?>>[]>();
+ for (Map.Entry<Object, Object> entry : constraintProperties.entrySet()) {
+
+ StringTokenizer tokens = new StringTokenizer((String) entry.getValue(), ", ");
+ LinkedList<Class<?>> classes = new LinkedList<Class<?>>();
+ while (tokens.hasMoreTokens()) {
+ final String eachClassName = tokens.nextToken();
+
+ Class<?> constraintValidatorClass =
+ run(new PrivilegedAction<Class<?>>() {
+ public Class<?> run() {
+ try {
+ return Class.forName(eachClassName, true, classloader);
+ } catch (ClassNotFoundException e) {
+ log.log(Level.SEVERE, String.format("Cannot find class %s", eachClassName), e);
+ return null;
+ }
+ }
+ });
+
+ if (constraintValidatorClass != null) classes.add(constraintValidatorClass);
+
+ }
+ loadedConstraints
+ .put((String) entry.getKey(),
+ (Class<? extends ConstraintValidator<?, ?>>[]) classes.toArray(new Class[classes.size()]));
+
+ }
+ return loadedConstraints;
+ }
+
+ private ClassLoader getClassLoader() {
+ ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+ if (classloader == null) classloader = getClass().getClassLoader();
+ return classloader;
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java
new file mode 100644
index 0000000..c3d4b86
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.Payload;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Description: immutable, serializable implementation of ConstraintDescriptor
+ * interface of JSR303<br>
+ * User: roman.stumm<br>
+ * Date: 22.04.2010<br>
+ * Time: 10:21:23<br>
+ */
+public class ConstraintDescriptorImpl<T extends Annotation> implements ConstraintDescriptor<T>, Serializable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ private final T annotation;
+ private final Set<Class<?>> groups;
+ private final Set<Class<? extends javax.validation.Payload>> payload;
+ private final List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> constraintValidatorClasses;
+ private final Map<String, Object> attributes;
+ private final Set<ConstraintDescriptor<?>> composingConstraints;
+ private final boolean reportAsSingleViolation;
+
+ /**
+ * Create a new ConstraintDescriptorImpl instance.
+ *
+ * @param descriptor
+ */
+ public ConstraintDescriptorImpl(ConstraintDescriptor<T> descriptor) {
+ this(descriptor.getAnnotation(), descriptor.getGroups(), descriptor.getPayload(), descriptor
+ .getConstraintValidatorClasses(), descriptor.getAttributes(), descriptor.getComposingConstraints(),
+ descriptor.isReportAsSingleViolation());
+ }
+
+ /**
+ * Create a new ConstraintDescriptorImpl instance.
+ *
+ * @param annotation
+ * @param groups
+ * @param payload
+ * @param constraintValidatorClasses
+ * @param attributes
+ * @param composingConstraints
+ * @param reportAsSingleViolation
+ */
+ public ConstraintDescriptorImpl(T annotation, Set<Class<?>> groups,
+ Set<Class<? extends javax.validation.Payload>> payload,
+ List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> constraintValidatorClasses,
+ Map<String, Object> attributes, Set<ConstraintDescriptor<?>> composingConstraints,
+ boolean reportAsSingleViolation) {
+ this.annotation = annotation;
+ this.groups = groups;
+ this.payload = payload;
+ this.constraintValidatorClasses = constraintValidatorClasses;
+ this.attributes = attributes;
+ this.composingConstraints = composingConstraints;
+ this.reportAsSingleViolation = reportAsSingleViolation;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T getAnnotation() {
+ return annotation;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<Class<?>> getGroups() {
+ return groups;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<Class<? extends Payload>> getPayload() {
+ return payload;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
+ return constraintValidatorClasses;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, Object> getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+ return composingConstraints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isReportAsSingleViolation() {
+ return reportAsSingleViolation;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java
new file mode 100644
index 0000000..fb8cd1a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.model.MetaBean;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+import javax.validation.metadata.Scope;
+import java.lang.annotation.ElementType;
+import java.util.*;
+
+/**
+ * Description: Implementation of the fluent {@link ConstraintFinder} interface.<br/>
+ */
+final class ConstraintFinderImpl implements ElementDescriptor.ConstraintFinder {
+ private final MetaBean metaBean;
+ private final Set<Scope> findInScopes;
+ private Set<ConstraintValidation<?>> constraintDescriptors;
+
+ /**
+ * Create a new ConstraintFinderImpl instance.
+ *
+ * @param metaBean
+ * @param constraintDescriptors
+ */
+ ConstraintFinderImpl(MetaBean metaBean, Set<ConstraintValidation<?>> constraintDescriptors) {
+ this.metaBean = metaBean;
+ this.constraintDescriptors = constraintDescriptors;
+ this.findInScopes = new HashSet<Scope>(Arrays.asList(Scope.values()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ElementDescriptor.ConstraintFinder unorderedAndMatchingGroups(Class<?>... groups) {
+ Set<ConstraintValidation<?>> matchingDescriptors =
+ new HashSet<ConstraintValidation<?>>(constraintDescriptors.size());
+ Groups groupChain = new GroupsComputer().computeGroups(groups);
+ for (Group group : groupChain.getGroups()) {
+ if (group.isDefault()) {
+ // If group is default, check if it gets redefined
+ List<Group> expandedDefaultGroup = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ for (Group defaultGroupMember : expandedDefaultGroup) {
+ for (ConstraintValidation<?> descriptor : constraintDescriptors) {
+ if (isInScope(descriptor) && isInGroup(descriptor, defaultGroupMember)) {
+ matchingDescriptors.add(descriptor);
+ }
+ }
+ }
+ } else {
+ for (ConstraintValidation<?> descriptor : constraintDescriptors) {
+ if (isInScope(descriptor) && isInGroup(descriptor, group)) {
+ matchingDescriptors.add(descriptor);
+ }
+ }
+ }
+ }
+ return thisWith(matchingDescriptors);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ElementDescriptor.ConstraintFinder lookingAt(Scope scope) {
+ if (scope.equals(Scope.LOCAL_ELEMENT)) {
+ findInScopes.remove(Scope.HIERARCHY);
+ for (Iterator<ConstraintValidation<?>> it = constraintDescriptors.iterator(); it.hasNext();) {
+ ConstraintValidation<?> cv = it.next();
+ if (cv.getOwner() != metaBean.getBeanClass()) {
+ it.remove();
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ElementDescriptor.ConstraintFinder declaredOn(ElementType... elementTypes) {
+ Set<ConstraintValidation<?>> matchingDescriptors =
+ new HashSet<ConstraintValidation<?>>(constraintDescriptors.size());
+ for (ElementType each : elementTypes) {
+ for (ConstraintValidation<?> descriptor : constraintDescriptors) {
+ if (isInScope(descriptor) && isAtElement(descriptor, each)) {
+ matchingDescriptors.add(descriptor);
+ }
+ }
+ }
+ return thisWith(matchingDescriptors);
+ }
+
+ private boolean isAtElement(ConstraintValidation<?> descriptor, ElementType each) {
+ return descriptor.getAccess().getElementType() == each;
+ }
+
+ private boolean isInScope(ConstraintValidation<?> descriptor) {
+ if (findInScopes.size() == Scope.values().length)
+ return true; // all scopes
+ if (metaBean != null) {
+ Class<?> owner = descriptor.getOwner();
+ for (Scope scope : findInScopes) {
+ switch (scope) {
+ case LOCAL_ELEMENT:
+ if (owner.equals(metaBean.getBeanClass()))
+ return true;
+ break;
+ case HIERARCHY:
+ if (!owner.equals(metaBean.getBeanClass()))
+ return true;
+ break;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isInGroup(ConstraintValidation<?> descriptor, Group group) {
+ return descriptor.getGroups().contains(group.getGroup());
+ }
+
+ private ElementDescriptor.ConstraintFinder thisWith(Set<ConstraintValidation<?>> matchingDescriptors) {
+ constraintDescriptors = matchingDescriptors;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+ return constraintDescriptors.isEmpty() ? Collections.<ConstraintDescriptor<?>> emptySet() : Collections
+ .<ConstraintDescriptor<?>> unmodifiableSet(constraintDescriptors);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasConstraints() {
+ return !constraintDescriptors.isEmpty();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
new file mode 100644
index 0000000..cd9ec70
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
@@ -0,0 +1,381 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintValidator;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+/**
+ * Description: Adapter between Constraint (JSR303) and Validation (Core)<br/>
+ * this instance is immutable!<br/>
+ */
+public class ConstraintValidation<T extends Annotation> implements Validation, ConstraintDescriptor<T> {
+ private final ConstraintValidator<T, ?> validator;
+ private T annotation; // for metadata request API
+ private final AccessStrategy access;
+ private final boolean reportFromComposite;
+ private final Map<String, Object> attributes;
+
+ private Set<ConstraintValidation<?>> composedConstraints;
+
+ /**
+ * the owner is the type where the validation comes from. it is used to
+ * support implicit grouping.
+ */
+ private final Class<?> owner;
+ private Set<Class<?>> groups;
+ private Set<Class<? extends Payload>> payload;
+ private Class<? extends ConstraintValidator<T, ?>>[] validatorClasses;
+
+ /**
+ * Create a new ConstraintValidation instance.
+ *
+ * @param validatorClasses
+ * @param validator
+ * - the constraint validator
+ * @param annotation
+ * - the annotation of the constraint
+ * @param owner
+ * - the type where the annotated element is placed (class,
+ * interface, annotation type)
+ * @param access
+ * - how to access the value
+ * @param reportFromComposite
+ */
+ public ConstraintValidation(Class<? extends ConstraintValidator<T, ?>>[] validatorClasses,
+ ConstraintValidator<T, ?> validator, T annotation, Class<?> owner, AccessStrategy access,
+ boolean reportFromComposite) {
+ this.attributes = new HashMap<String, Object>();
+ this.validatorClasses = ArrayUtils.clone(validatorClasses);
+ this.validator = validator;
+ this.annotation = annotation;
+ this.owner = owner;
+ this.access = access;
+ this.reportFromComposite = reportFromComposite;
+ }
+
+ /**
+ * Return a {@link Serializable} {@link ConstraintDescriptor} capturing a
+ * snapshot of current state.
+ *
+ * @return {@link ConstraintDescriptor}
+ */
+ public ConstraintDescriptor<T> asSerializableDescriptor() {
+ return new ConstraintDescriptorImpl<T>(this);
+ }
+
+ /**
+ * Set the applicable validation groups.
+ *
+ * @param groups
+ */
+ void setGroups(Set<Class<?>> groups) {
+ this.groups = groups;
+ ConstraintAnnotationAttributes.GROUPS.put(attributes, groups.toArray(new Class[groups.size()]));
+ }
+
+ /**
+ * Set the payload.
+ *
+ * @param payload
+ */
+ void setPayload(Set<Class<? extends Payload>> payload) {
+ this.payload = payload;
+ ConstraintAnnotationAttributes.PAYLOAD.put(attributes, payload.toArray(new Class[payload.size()]));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isReportAsSingleViolation() {
+ return reportFromComposite;
+ }
+
+ /**
+ * Add a composing constraint.
+ *
+ * @param aConstraintValidation
+ * to add
+ */
+ public void addComposed(ConstraintValidation<?> aConstraintValidation) {
+ if (composedConstraints == null) {
+ composedConstraints = new HashSet<ConstraintValidation<?>>();
+ }
+ composedConstraints.add(aConstraintValidation);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <L extends ValidationListener> void validate(ValidationContext<L> context) {
+ validate((GroupValidationContext<?>) context);
+ }
+
+ /**
+ * Validate a {@link GroupValidationContext}.
+ *
+ * @param context
+ * root
+ */
+ public void validate(GroupValidationContext<?> context) {
+ context.setConstraintValidation(this);
+ /**
+ * execute unless the given validation constraint has already been
+ * processed during this validation routine (as part of a previous group
+ * match)
+ */
+ if (!isMemberOf(context.getCurrentGroup().getGroup())) {
+ return; // do not validate in the current group
+ }
+ if (context.getCurrentOwner() != null && this.owner != context.getCurrentOwner()) {
+ return;
+ }
+ if (validator != null && !context.collectValidated(validator))
+ return; // already done
+
+ if (context.getMetaProperty() != null && !isReachable(context)) {
+ return;
+ }
+
+ // process composed constraints
+ if (isReportAsSingleViolation()) {
+ ConstraintValidationListener<?> listener = context.getListener();
+ listener.beginReportAsSingle();
+
+ boolean failed = listener.hasViolations();
+ try {
+ // stop validating when already failed and
+ // ReportAsSingleInvalidConstraint = true ?
+ for (Iterator<ConstraintValidation<?>> composed = getComposingValidations().iterator(); !failed && composed.hasNext();) {
+ composed.next().validate(context);
+ failed = listener.hasViolations();
+ }
+ } finally {
+ listener.endReportAsSingle();
+ // Restore current constraint validation
+ context.setConstraintValidation(this);
+ }
+
+ if (failed) {
+ // TODO RSt - how should the composed constraint error report look like?
+ ConstraintValidatorContextImpl jsrContext = new ConstraintValidatorContextImpl(context, this);
+ addErrors(context, jsrContext); // add defaultErrorMessage only
+ return;
+ }
+ } else {
+ for (ConstraintValidation<?> composed : getComposingValidations()) {
+ composed.validate(context);
+ }
+
+ // Restore current constraint validation
+ context.setConstraintValidation(this);
+ }
+
+ if (validator != null) {
+ ConstraintValidatorContextImpl jsrContext = new ConstraintValidatorContextImpl(context, this);
+ @SuppressWarnings("unchecked")
+ final ConstraintValidator<T, Object> objectValidator = (ConstraintValidator<T, Object>) validator;
+ if (!objectValidator.isValid(context.getValidatedValue(), jsrContext)) {
+ addErrors(context, jsrContext);
+ }
+ }
+ }
+
+ /**
+ * Initialize the validator (if not <code>null</code>) with the stored
+ * annotation.
+ */
+ public void initialize() {
+ if (null != validator) {
+ try {
+ validator.initialize(annotation);
+ } catch (RuntimeException e) {
+ // Either a "legit" problem initializing the validator or a
+ // ClassCastException if the validator associated annotation is
+ // not a supertype of the validated annotation.
+ throw new ConstraintDefinitionException("Incorrect validator ["
+ + validator.getClass().getCanonicalName() + "] for annotation "
+ + annotation.annotationType().getCanonicalName(), e);
+ }
+ }
+ }
+
+ private boolean isReachable(GroupValidationContext<?> context) {
+ PathImpl path = context.getPropertyPath();
+ NodeImpl node = path.getLeafNode();
+ PathImpl beanPath = path.getPathWithoutLeafNode();
+ if (beanPath == null) {
+ beanPath = PathImpl.create(null);
+ }
+ try {
+ if (!context.getTraversableResolver().isReachable(context.getBean(), node,
+ context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+ return false;
+ } catch (RuntimeException e) {
+ throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e);
+ }
+
+ return true;
+ }
+
+ private void addErrors(GroupValidationContext<?> context, ConstraintValidatorContextImpl jsrContext) {
+ for (ValidationListener.Error each : jsrContext.getErrorMessages()) {
+ context.getListener().addError(each, context);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "ConstraintValidation{" + validator + '}';
+ }
+
+ /**
+ * Get the message template used by this constraint.
+ *
+ * @return String
+ */
+ public String getMessageTemplate() {
+ return ConstraintAnnotationAttributes.MESSAGE.get(attributes);
+ }
+
+ /**
+ * Get the {@link ConstraintValidator} invoked by this
+ * {@link ConstraintValidation}.
+ *
+ * @return
+ */
+ public ConstraintValidator<T, ?> getValidator() {
+ return validator;
+ }
+
+ /**
+ * Learn whether this {@link ConstraintValidation} belongs to the specified
+ * group.
+ *
+ * @param reqGroup
+ * @return boolean
+ */
+ protected boolean isMemberOf(Class<?> reqGroup) {
+ return groups.contains(reqGroup);
+ }
+
+ /**
+ * Get the owning class of this {@link ConstraintValidation}.
+ *
+ * @return Class
+ */
+ public Class<?> getOwner() {
+ return owner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public T getAnnotation() {
+ return annotation;
+ }
+
+ /**
+ * Get the {@link AccessStrategy} used by this {@link ConstraintValidation}.
+ *
+ * @return {@link AccessStrategy}
+ */
+ public AccessStrategy getAccess() {
+ return access;
+ }
+
+ /**
+ * Override the Annotation set at construction.
+ *
+ * @param annotation
+ */
+ public void setAnnotation(T annotation) {
+ this.annotation = annotation;
+ }
+
+ // ///////////////////////// ConstraintDescriptor implementation
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, Object> getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+ return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+ }
+
+ /**
+ * Get the composing {@link ConstraintValidation} objects. This is
+ * effectively an implementation-specific analogue to
+ * {@link #getComposingConstraints()}.
+ *
+ * @return {@link Set} of {@link ConstraintValidation}
+ */
+ @SuppressWarnings("unchecked")
+ Set<ConstraintValidation<?>> getComposingValidations() {
+ return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<Class<?>> getGroups() {
+ return groups;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<Class<? extends Payload>> getPayload() {
+ return payload;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
+ if (validatorClasses == null) {
+ return Collections.emptyList();
+ }
+ return Arrays.asList(validatorClasses);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
new file mode 100644
index 0000000..cf0972b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.lang.annotation.ElementType;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: JSR-303 {@link ValidationListener} implementation; provides {@link ConstraintViolation}s.<br/>
+ *
+ * @version $Rev$ $Date$
+ */
+public final class ConstraintValidationListener<T> implements ValidationListener {
+ private final Set<ConstraintViolation<T>> constraintViolations = new HashSet<ConstraintViolation<T>>();
+ private final T rootBean;
+ private final Class<T> rootBeanType;
+ // the validation process is single-threaded and it's unlikely to change in the near future (otherwise use AtomicInteger).
+ private int compositeDepth = 0;
+ private boolean hasCompositeError;
+
+ /**
+ * Create a new ConstraintValidationListener instance.
+ * @param aRootBean
+ * @param rootBeanType
+ */
+ public ConstraintValidationListener(T aRootBean, Class<T> rootBeanType) {
+ this.rootBean = aRootBean;
+ this.rootBeanType = rootBeanType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <VL extends ValidationListener> void addError(String reason, ValidationContext<VL> context) {
+ addError(reason, null, context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <VL extends ValidationListener> void addError(Error error, ValidationContext<VL> context) {
+ if (error.getOwner() instanceof Path) {
+ addError(error.getReason(), (Path) error.getOwner(), context);
+ } else {
+ addError(error.getReason(), null, context);
+ }
+ }
+
+ private void addError(String messageTemplate, Path propPath,
+ ValidationContext<?> context) {
+ if (compositeDepth > 0) {
+ hasCompositeError |= true;
+ return;
+ }
+ final Object value;
+
+ final ConstraintDescriptor<?> descriptor;
+ final String message;
+ if (context instanceof GroupValidationContext<?>) {
+ GroupValidationContext<?> gcontext = (GroupValidationContext<?>) context;
+ value = gcontext.getValidatedValue();
+ if (gcontext instanceof MessageInterpolator.Context) {
+ message = gcontext.getMessageResolver()
+ .interpolate(messageTemplate,
+ (MessageInterpolator.Context) gcontext);
+ } else {
+ message =
+ gcontext.getMessageResolver().interpolate(messageTemplate, null);
+ }
+ descriptor = gcontext.getConstraintValidation().asSerializableDescriptor();
+ if (propPath == null) propPath = gcontext.getPropertyPath();
+ } else {
+ if (context.getMetaProperty() == null) value = context.getBean();
+ else value = context.getPropertyValue();
+ message = messageTemplate;
+ if (propPath == null)
+ propPath = PathImpl.createPathFromString(context.getPropertyName());
+ descriptor = null;
+ }
+ ElementType elementType = (context.getAccess() != null) ? context.getAccess().getElementType() : null;
+ ConstraintViolationImpl<T> ic = new ConstraintViolationImpl<T>(messageTemplate,
+ message, rootBean, context.getBean(), propPath, value, descriptor, rootBeanType, elementType);
+ constraintViolations.add(ic);
+ }
+
+ /**
+ * Get the {@link ConstraintViolation}s accumulated by this {@link ConstraintValidationListener}.
+ * @return {@link Set} of {@link ConstraintViolation}
+ */
+ public Set<ConstraintViolation<T>> getConstraintViolations() {
+ return constraintViolations;
+ }
+
+ /**
+ * Learn whether no violations were found.
+ * @return boolean
+ */
+ public boolean isEmpty() {
+ return constraintViolations.isEmpty();
+ }
+
+ /**
+ * Get the root bean.
+ * @return T
+ */
+ public T getRootBean() {
+ return rootBean;
+ }
+
+ /**
+ * Get the root bean type of this {@link ConstraintValidationListener}.
+ * @return Class<T>
+ */
+ public Class<T> getRootBeanType() {
+ return rootBeanType;
+ }
+
+ /**
+ * Get the count of encountered violations.
+ * @return int
+ */
+ public int violationsSize() {
+ return constraintViolations.size();
+ }
+
+ /**
+ * Learn whether there are violations available.
+ * If in report-as-single-violation mode, the result is scoped accordingly.
+ * Note that this means you must check before exiting report-as-single-violation mode
+ * @return boolean
+ */
+ public boolean hasViolations() {
+ return compositeDepth == 0 ? !constraintViolations.isEmpty() : hasCompositeError;
+ }
+
+ /**
+ * Signify the beginning of a report-as-single-violation composite validation.
+ * @return <code>true</code> as this call caused the listener to enter report-as-single-violation mode
+ */
+ public boolean beginReportAsSingle() {
+ return ++compositeDepth == 1;
+ }
+
+ /**
+ * Signify the end of a report-as-single-violation composite validation.
+ * @return <code>true</code> as this call caused the listener to exit report-as-single-violation mode
+ */
+ public boolean endReportAsSingle() {
+ return --compositeDepth == 0;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java
new file mode 100644
index 0000000..cad7941
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+
+import org.apache.bval.jsr303.util.NodeBuilderDefinedContextImpl;
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.ValidationListener;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Path;
+import javax.validation.ValidationException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Description: Short-lived {@link ConstraintValidatorContext} implementation passed by
+ * a {@link ConstraintValidation} to its adapted {@link ConstraintValidator}. <br/>
+ */
+public class ConstraintValidatorContextImpl implements ConstraintValidatorContext {
+ private final List<ValidationListener.Error> errorMessages =
+ new LinkedList<ValidationListener.Error>();
+
+ private final ConstraintValidation<?> constraintDescriptor;
+ private final GroupValidationContext<?> validationContext;
+
+ private boolean defaultDisabled;
+
+ /**
+ * Create a new ConstraintValidatorContextImpl instance.
+ * @param validationContext
+ * @param aConstraintValidation
+ */
+ public ConstraintValidatorContextImpl(GroupValidationContext<?> validationContext,
+ ConstraintValidation<?> aConstraintValidation) {
+ this.validationContext = validationContext;
+ this.constraintDescriptor = aConstraintValidation;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void disableDefaultConstraintViolation() {
+ defaultDisabled = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDefaultConstraintMessageTemplate() {
+ return constraintDescriptor.getMessageTemplate();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintViolationBuilder buildConstraintViolationWithTemplate(
+ String messageTemplate) {
+ return new ConstraintViolationBuilderImpl(this, messageTemplate,
+ validationContext.getPropertyPath());
+ }
+
+ private static final class ConstraintViolationBuilderImpl
+ implements ConstraintValidatorContext.ConstraintViolationBuilder {
+ private final ConstraintValidatorContextImpl parent;
+ private final String messageTemplate;
+ private final PathImpl propertyPath;
+
+ /**
+ * Create a new ConstraintViolationBuilderImpl instance.
+ * @param contextImpl
+ * @param template
+ * @param path
+ */
+ ConstraintViolationBuilderImpl(ConstraintValidatorContextImpl contextImpl,
+ String template, PathImpl path) {
+ parent = contextImpl;
+ messageTemplate = template;
+ propertyPath = path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public NodeBuilderDefinedContext addNode(String name) {
+ PathImpl path;
+ if (propertyPath.isRootPath()) {
+ path = PathImpl.create(name);
+ } else {
+ path = PathImpl.copy(propertyPath);
+ path.addNode(new NodeImpl(name));
+ }
+ return new NodeBuilderDefinedContextImpl(parent, messageTemplate, path);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext addConstraintViolation() {
+ parent.addError(messageTemplate, propertyPath);
+ return parent;
+ }
+ }
+
+ /**
+ * Get the queued error messages.
+ * @return List
+ */
+ public List<ValidationListener.Error> getErrorMessages() {
+ if (defaultDisabled && errorMessages.isEmpty()) {
+ throw new ValidationException(
+ "At least one custom message must be created if the default error message gets disabled.");
+ }
+
+ List<ValidationListener.Error> returnedErrorMessages =
+ new ArrayList<ValidationListener.Error>(errorMessages);
+ if (!defaultDisabled) {
+ returnedErrorMessages.add(new ValidationListener.Error(
+ getDefaultConstraintMessageTemplate(), validationContext.getPropertyPath(),
+ null));
+ }
+ return returnedErrorMessages;
+ }
+
+ /**
+ * Get this {@link ConstraintValidatorContext}'s {@link GroupValidationContext}.
+ * @return {@link GroupValidationContext}
+ */
+ public GroupValidationContext<?> getValidationContext() {
+ return validationContext;
+ }
+
+ /**
+ * Add an error message to this {@link ConstraintValidatorContext}.
+ * @param messageTemplate
+ * @param propertyPath
+ */
+ public void addError(String messageTemplate, Path propertyPath) {
+ errorMessages.add(new ValidationListener.Error(messageTemplate, propertyPath, null));
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorIdentity.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorIdentity.java
new file mode 100644
index 0000000..7e4b6fa
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorIdentity.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.Path;
+
+/**
+ * Class that stores the needed properties to ensure that a validation is not
+ * checked more than once.
+ * <p>
+ * These properties are:
+ * <ul>
+ * <li>The ref of the bean to which the validation would be applied.</li>
+ * <li>The path of the property.</li>
+ * <li>The ref of the {@link ConstraintValidator}.</li>
+ * </ul>
+ *
+ * @author Carlos Vara
+ */
+final class ConstraintValidatorIdentity {
+
+ private final Object bean;
+ private final Path path;
+ private final ConstraintValidator<?, ?> constraintValidator;
+
+ /**
+ * Create a new ConstraintValidatorIdentity instance.
+ * @param bean
+ * @param path
+ * @param constraintValidator
+ */
+ public ConstraintValidatorIdentity(Object bean, Path path, ConstraintValidator<?, ?> constraintValidator) {
+ this.bean = bean;
+ this.path = path;
+ this.constraintValidator = constraintValidator;
+ }
+
+ /**
+ * Get the referenced bean.
+ * @return Object
+ */
+ public Object getBean() {
+ return bean;
+ }
+
+ /**
+ * Get the referenced property {@link Path}.
+ * @return Path
+ */
+ public Path getPath() {
+ return path;
+ }
+
+ /**
+ * Get the associated {@link ConstraintValidator}.
+ * @return {@link ConstraintValidator}
+ */
+ public ConstraintValidator<?, ?> getConstraintValidator() {
+ return constraintValidator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (!(obj instanceof ConstraintValidatorIdentity)) {
+ return false;
+ }
+
+ ConstraintValidatorIdentity other = (ConstraintValidatorIdentity) obj;
+
+ // Bean ref must be the same
+ if (this.bean != other.bean) {
+ return false;
+ }
+
+ // ConstraintValidator ref must be the same
+ if (this.constraintValidator != other.constraintValidator) {
+ return false;
+ }
+
+ // Path must be equals
+ if (!this.path.equals(other.path)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((this.bean == null) ? 0 : this.bean.hashCode());
+ result = prime * result
+ + ((this.path == null) ? 0 : this.path.hashCode());
+ result = prime * result
+ + ((this.constraintValidator == null) ? 0 : this.constraintValidator.hashCode());
+ return result;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java
new file mode 100644
index 0000000..1ced366
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.ElementType;
+
+/**
+ * Description: Describe a constraint validation defect.<br/>
+ * From rootBean and propertyPath, it is possible to rebuild the context of the failure
+ */
+class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ private final String messageTemplate;
+ private final String message;
+ /** root bean validation was invoked on. */
+ private final T rootBean;
+ private final Class<T> rootBeanClass;
+ /** last bean validated. */
+ private final Object leafBean;
+ private final Object value;
+ private final Path propertyPath;
+ private final ElementType elementType;
+ private final ConstraintDescriptor<?> constraintDescriptor;
+
+ /**
+ * Create a new ConstraintViolationImpl instance.
+ * @param messageTemplate - message reason (raw message)
+ * @param message - interpolated message (locale specific)
+ * @param rootBean
+ * @param leafBean
+ * @param propertyPath
+ * @param value
+ * @param constraintDescriptor
+ * @param rootBeanClass
+ * @param elementType
+ */
+ public ConstraintViolationImpl(String messageTemplate, String message, T rootBean, Object leafBean,
+ Path propertyPath, Object value,
+ ConstraintDescriptor<?> constraintDescriptor, Class<T> rootBeanClass, ElementType elementType) {
+ this.messageTemplate = messageTemplate;
+ this.message = message;
+ this.rootBean = rootBean;
+ this.rootBeanClass = rootBeanClass;
+ this.propertyPath = propertyPath;
+ this.leafBean = leafBean;
+ this.value = value;
+ this.constraintDescriptor = constraintDescriptor;
+ this.elementType = elementType;
+ }
+
+ /**
+ * {@inheritDoc}
+ * former name getInterpolatedMessage()
+ * @return The interpolated error message for this constraint violation.
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getMessageTemplate() {
+ return messageTemplate;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return Root bean being validated
+ */
+ public T getRootBean() {
+ return rootBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<T> getRootBeanClass() {
+ return rootBeanClass;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getLeafBean() {
+ return leafBean;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return The value failing to pass the constraint
+ */
+ public Object getInvalidValue() {
+ return value;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return the property path to the value from <code>rootBean</code>
+ * Null if the value is the rootBean itself
+ */
+ public Path getPropertyPath() {
+ return propertyPath;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return constraintDescriptor;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "ConstraintViolationImpl{" + "rootBean=" + rootBean + ", propertyPath='" +
+ propertyPath + '\'' + ", message='" + message + '\'' + ", leafBean=" +
+ leafBean + ", value=" + value + '}';
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((this.leafBean == null) ? 0 : this.leafBean.hashCode());
+ result = prime * result
+ + ((this.message == null) ? 0 : this.message.hashCode());
+ result = prime
+ * result
+ + ((this.propertyPath == null) ? 0 : this.propertyPath
+ .hashCode());
+ result = prime * result
+ + ((this.rootBean == null) ? 0 : this.rootBean.hashCode());
+ result = prime * result
+ + ((this.value == null) ? 0 : this.value.hashCode());
+ result = prime * result
+ + ((this.elementType == null) ? 0 : this.elementType.hashCode());
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ * NOTE: Needed to avoid duplication in the reported violations.
+ *
+ * @param obj the reference object with which to compare.
+ * @return <code>true</code> if this object is the same as the obj
+ * argument; <code>false</code> otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof ConstraintViolationImpl<?>)) {
+ return false;
+ }
+
+ ConstraintViolationImpl<?> other = (ConstraintViolationImpl<?>) obj;
+
+ if (this.leafBean == null) {
+ if (other.leafBean != null) {
+ return false;
+ }
+ } else if (!this.leafBean.equals(other.leafBean)) {
+ return false;
+ }
+
+ if (this.message == null) {
+ if (other.message != null) {
+ return false;
+ }
+ } else if (!this.message.equals(other.message)) {
+ return false;
+ }
+
+ if (this.propertyPath == null) {
+ if (other.propertyPath != null) {
+ return false;
+ }
+ } else if (!this.propertyPath.equals(other.propertyPath)) {
+ return false;
+ }
+
+ if (this.rootBean == null) {
+ if (other.rootBean != null) {
+ return false;
+ }
+ } else if (!this.rootBean.equals(other.rootBean)) {
+ return false;
+ }
+
+ if (this.rootBeanClass != other.rootBeanClass) {
+ return false;
+ }
+
+ if (this.value == null) {
+ if (other.value != null) {
+ return false;
+ }
+ } else if (!this.value.equals(other.value)) {
+ return false;
+ }
+
+ if (this.elementType != other.elementType) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultConstraintValidatorFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultConstraintValidatorFactory.java
new file mode 100644
index 0000000..a67ff7b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultConstraintValidatorFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.ValidationException;
+
+
+/**
+ * Description: create constraint instances with the default / no-arg constructor <br/>
+ */
+public class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory {
+
+ /**
+ * Instantiate a Constraint.
+ *
+ * @return Returns a new Constraint instance
+ * The ConstraintFactory is <b>not</b> responsible for calling Constraint#initialize
+ */
+ public <T extends ConstraintValidator<?, ?>> T getInstance(final Class<T> constraintClass)
+ {
+ // 2011-03-27 jw: Do not use PrivilegedAction.
+ // Otherwise any user code would be executed with the privileges of this class.
+ try
+ {
+ return constraintClass.newInstance();
+ }
+ catch (final Exception ex)
+ {
+ throw new ValidationException("Cannot instantiate : " + constraintClass, ex);
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultMessageInterpolator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultMessageInterpolator.java
new file mode 100644
index 0000000..0a341a6
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultMessageInterpolator.java
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.validation.MessageInterpolator;
+
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: Resource bundle backed message interpolator.
+ * This message resolver resolve message descriptors
+ * into human-readable messages. It uses ResourceBundles to find the messages.
+ * This class is threadsafe.<br/>
+ */
+public class DefaultMessageInterpolator implements MessageInterpolator {
+ private static final Logger log = Logger.getLogger(DefaultMessageInterpolator.class.getName());
+ private static final String DEFAULT_VALIDATION_MESSAGES =
+ "org.apache.bval.jsr303.ValidationMessages";
+ private static final String USER_VALIDATION_MESSAGES = "ValidationMessages";
+
+ /** Regular expression used to do message interpolation. */
+ private static final Pattern messageParameterPattern =
+ Pattern.compile("(\\{[\\w\\.]+\\})");
+
+ /** The default locale for the current user. */
+ private Locale defaultLocale;
+
+ /** User specified resource bundles hashed against their locale. */
+ private final Map<Locale, ResourceBundle> userBundlesMap =
+ new ConcurrentHashMap<Locale, ResourceBundle>();
+
+ /** Builtin resource bundles hashed against their locale. */
+ private final Map<Locale, ResourceBundle> defaultBundlesMap =
+ new ConcurrentHashMap<Locale, ResourceBundle>();
+
+ /**
+ * Create a new DefaultMessageInterpolator instance.
+ */
+ public DefaultMessageInterpolator() {
+ this(null);
+ }
+
+ /**
+ * Create a new DefaultMessageInterpolator instance.
+ * @param resourceBundle
+ */
+ public DefaultMessageInterpolator(ResourceBundle resourceBundle) {
+
+ defaultLocale = Locale.getDefault();
+
+ if (resourceBundle == null) {
+ ResourceBundle bundle = getFileBasedResourceBundle(defaultLocale);
+ if (bundle != null) {
+ userBundlesMap.put(defaultLocale, bundle);
+ }
+
+ } else {
+ userBundlesMap.put(defaultLocale, resourceBundle);
+ }
+
+ defaultBundlesMap.put(defaultLocale,
+ ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, defaultLocale));
+ }
+
+ /** {@inheritDoc} */
+ public String interpolate(String message, Context context) {
+ // probably no need for caching, but it could be done by parameters since the map
+ // is immutable and uniquely built per Validation definition, the comparison has to be based on == and not equals though
+ return interpolate(message, context, defaultLocale);
+ }
+
+ /** {@inheritDoc} */
+ public String interpolate(String message, Context context, Locale locale) {
+ return interpolateMessage(message,
+ context.getConstraintDescriptor().getAttributes(), locale);
+ }
+
+ /**
+ * Runs the message interpolation according to algorithm specified in JSR 303.
+ * <br/>
+ * Note:
+ * <br/>
+ * Lookups in user bundles are recursive whereas lookups in default bundle are not!
+ *
+ * @param message the message to interpolate
+ * @param annotationParameters the parameters of the annotation for which to interpolate this message
+ * @param locale the <code>Locale</code> to use for the resource bundle.
+ * @return the interpolated message.
+ */
+ private String interpolateMessage(String message,
+ Map<String, Object> annotationParameters,
+ Locale locale) {
+ ResourceBundle userResourceBundle = findUserResourceBundle(locale);
+ ResourceBundle defaultResourceBundle = findDefaultResourceBundle(locale);
+
+ String userBundleResolvedMessage;
+ String resolvedMessage = message;
+ boolean evaluatedDefaultBundleOnce = false;
+ do {
+ // search the user bundle recursive (step1)
+ userBundleResolvedMessage =
+ replaceVariables(resolvedMessage, userResourceBundle, locale, true);
+
+ // exit condition - we have at least tried to validate against the default bundle and there were no
+ // further replacements
+ if (evaluatedDefaultBundleOnce &&
+ !hasReplacementTakenPlace(userBundleResolvedMessage, resolvedMessage)) {
+ break;
+ }
+
+ // search the default bundle non recursive (step2)
+ resolvedMessage = replaceVariables(userBundleResolvedMessage,
+ defaultResourceBundle, locale, false);
+
+ evaluatedDefaultBundleOnce = true;
+ } while (true);
+
+ // resolve annotation attributes (step 4)
+ resolvedMessage =
+ replaceAnnotationAttributes(resolvedMessage, annotationParameters);
+
+ // curly braces need to be scaped in the original msg, so unescape them now
+ resolvedMessage = resolvedMessage.replace( "\\{", "{" ).replace( "\\}", "}" ).replace( "\\\\", "\\" );
+
+ return resolvedMessage;
+ }
+
+ private boolean hasReplacementTakenPlace(String origMessage, String newMessage) {
+ return !origMessage.equals(newMessage);
+ }
+
+ /**
+ * Search current thread classloader for the resource bundle. If not found, search validator (this) classloader.
+ *
+ * @param locale The locale of the bundle to load.
+ * @return the resource bundle or <code>null</code> if none is found.
+ */
+ private ResourceBundle getFileBasedResourceBundle(Locale locale) {
+ ResourceBundle rb = null;
+ final ClassLoader classLoader = doPrivileged(SecureActions.getContextClassLoader());
+ if (classLoader != null) {
+ rb = loadBundle(classLoader, locale,
+ USER_VALIDATION_MESSAGES + " not found by thread local classloader");
+ }
+
+ // 2011-03-27 jw: No privileged action required.
+ // A class can always access the classloader of itself and of subclasses.
+ if (rb == null) {
+ rb = loadBundle(
+ getClass().getClassLoader(),
+ locale,
+ USER_VALIDATION_MESSAGES + " not found by validator classloader"
+ );
+ }
+ if (rb != null) {
+ log.log(Level.FINEST, String.format("%s found", USER_VALIDATION_MESSAGES));
+ } else {
+ log.log(Level.FINEST, String.format("%s not found. Delegating to %s", USER_VALIDATION_MESSAGES, DEFAULT_VALIDATION_MESSAGES));
+ }
+ return rb;
+ }
+
+ private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale,
+ String message) {
+ ResourceBundle rb = null;
+ try {
+ rb = ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, classLoader);
+ } catch (MissingResourceException e) {
+ log.fine(message);
+ }
+ return rb;
+ }
+
+ private String replaceVariables(String message, ResourceBundle bundle, Locale locale,
+ boolean recurse) {
+ Matcher matcher = messageParameterPattern.matcher(message);
+ StringBuffer sb = new StringBuffer(64);
+ String resolvedParameterValue;
+ while (matcher.find()) {
+ String parameter = matcher.group(1);
+ resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse);
+
+ matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue));
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ private String replaceAnnotationAttributes(String message,
+ Map<String, Object> annotationParameters) {
+ Matcher matcher = messageParameterPattern.matcher(message);
+ StringBuffer sb = new StringBuffer(64);
+ while (matcher.find()) {
+ String resolvedParameterValue;
+ String parameter = matcher.group(1);
+ Object variable = annotationParameters.get(removeCurlyBrace(parameter));
+ if (variable != null) {
+ if (variable.getClass().isArray()) {
+ resolvedParameterValue = ArrayUtils.toString(variable);
+ } else {
+ resolvedParameterValue = variable.toString();
+ }
+ } else {
+ resolvedParameterValue = parameter;
+ }
+ matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue));
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ private String resolveParameter(String parameterName, ResourceBundle bundle,
+ Locale locale, boolean recurse) {
+ String parameterValue;
+ try {
+ if (bundle != null) {
+ parameterValue = bundle.getString(removeCurlyBrace(parameterName));
+ if (recurse) {
+ parameterValue =
+ replaceVariables(parameterValue, bundle, locale, recurse);
+ }
+ } else {
+ parameterValue = parameterName;
+ }
+ } catch (MissingResourceException e) {
+ // return parameter itself
+ parameterValue = parameterName;
+ }
+ return parameterValue;
+ }
+
+ private String removeCurlyBrace(String parameter) {
+ return parameter.substring(1, parameter.length() - 1);
+ }
+
+ private ResourceBundle findDefaultResourceBundle(Locale locale) {
+ ResourceBundle bundle = defaultBundlesMap.get(locale);
+ if (bundle == null)
+ {
+ bundle = ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, locale);
+ defaultBundlesMap.put(locale, bundle);
+ }
+ return bundle;
+ }
+
+ private ResourceBundle findUserResourceBundle(Locale locale) {
+ ResourceBundle bundle = userBundlesMap.get(locale);
+ if (bundle == null)
+ {
+ bundle = getFileBasedResourceBundle(locale);
+ if (bundle != null) {
+ userBundlesMap.put(locale, bundle);
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * Set the default locale used by this {@link DefaultMessageInterpolator}.
+ * @param locale
+ */
+ public void setLocale(Locale locale) {
+ defaultLocale = locale;
+ }
+
+ /**
+ * Escapes the string to comply with
+ * {@link Matcher#appendReplacement(StringBuffer, String)} requirements.
+ *
+ * @param src
+ * The original string.
+ * @return The sanitized string.
+ */
+ private String sanitizeForAppendReplacement(String src) {
+ return src.replace("\\", "\\\\").replace("$", "\\$");
+ }
+
+
+
+ /**
+ * Perform action with AccessController.doPrivileged() if a security manager is installed.
+ *
+ * @param action
+ * the action to run
+ * @return
+ * result of the action
+ */
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultValidationProviderResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultValidationProviderResolver.java
new file mode 100644
index 0000000..30e6333
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultValidationProviderResolver.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+
+import javax.validation.ValidationException;
+import javax.validation.ValidationProviderResolver;
+import javax.validation.spi.ValidationProvider;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+public class DefaultValidationProviderResolver implements ValidationProviderResolver {
+
+ //TODO - Spec recommends caching per classloader
+ private static final String SPI_CFG =
+ "META-INF/services/javax.validation.spi.ValidationProvider";
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<ValidationProvider<?>> getValidationProviders() {
+ List<ValidationProvider<?>> providers = new ArrayList<ValidationProvider<?>>();
+ try {
+ // get our classloader
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ if (cl == null)
+ cl = DefaultValidationProviderResolver.class.getClassLoader();
+ // find all service provider cfgs
+ Enumeration<URL> cfgs = cl.getResources(SPI_CFG);
+ while (cfgs.hasMoreElements()) {
+ URL url = cfgs.nextElement();
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new InputStreamReader(url.openStream()), 256);
+ String line = br.readLine();
+ // cfgs may contain multiple providers and/or comments
+ while (line != null) {
+ line = line.trim();
+ if (!line.startsWith("#")) {
+ try {
+ // try loading the specified class
+ final Class<?> provider = cl.loadClass(line);
+ // create an instance to return
+ ValidationProvider<?> vp =
+ AccessController.doPrivileged(new PrivilegedAction<ValidationProvider<?>>() {
+ public ValidationProvider<?> run() {
+ try {
+ return (ValidationProvider<?>) provider.newInstance();
+ } catch (final Exception ex) {
+ throw new ValidationException("Cannot instantiate : " + provider, ex);
+ }
+ }
+ });
+ providers.add(vp);
+
+ } catch (ClassNotFoundException e) {
+ throw new ValidationException("Failed to load provider " +
+ line + " configured in file " + url, e);
+ }
+ }
+ line = br.readLine();
+ }
+ br.close();
+ } catch (IOException e) {
+ throw new ValidationException("Error trying to read " + url, e);
+ } finally {
+ if (br != null)
+ br.close();
+ }
+ }
+ } catch (IOException e) {
+ throw new ValidationException("Error trying to read a " + SPI_CFG, e);
+ }
+ // caller must handle the case of no providers found
+ return providers;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ElementDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ElementDescriptorImpl.java
new file mode 100644
index 0000000..7804e5b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ElementDescriptorImpl.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: MetaData class<br/>
+ */
+public abstract class ElementDescriptorImpl implements ElementDescriptor {
+
+ /**
+ * Get a set of {@link ConstraintDescriptor}s from the specified array of
+ * {@link Validation}s.
+ *
+ * @param validations
+ * @return {@link ConstraintDescriptor} set
+ */
+ protected static Set<ConstraintDescriptor<?>> getConstraintDescriptors(Validation[] validations) {
+ final Set<ConstraintDescriptor<?>> result = new HashSet<ConstraintDescriptor<?>>(validations.length);
+ for (Validation validation : validations) {
+ if (validation instanceof ConstraintValidation<?>) {
+ result.add((ConstraintValidation<?>) validation);
+ }
+ }
+ return result;
+ }
+
+ /** the MetaBean of this element */
+ protected final MetaBean metaBean;
+
+ /** the raw type of this element */
+ protected final Class<?> elementClass;
+
+ private Set<ConstraintDescriptor<?>> constraintDescriptors;
+
+ /**
+ * Create a new ElementDescriptorImpl instance.
+ *
+ * @param metaBean
+ * @param elementClass
+ * @param validations
+ */
+ protected ElementDescriptorImpl(MetaBean metaBean, Class<?> elementClass, Validation[] validations) {
+ this.metaBean = metaBean;
+ this.elementClass = elementClass;
+ setConstraintDescriptors(getConstraintDescriptors(validations));
+ }
+
+ /**
+ * Create a new ElementDescriptorImpl instance.
+ *
+ * @param elementClass
+ * @param validations
+ */
+ protected ElementDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+ this(null, elementClass, validations);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return Statically defined returned type.
+ */
+ public Class<?> getElementClass() {
+ return elementClass;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public ElementDescriptor.ConstraintFinder findConstraints() {
+ return new ConstraintFinderImpl(metaBean, new HashSet((Set) constraintDescriptors));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+ return constraintDescriptors.isEmpty() ? Collections.<ConstraintDescriptor<?>> emptySet() : Collections
+ .unmodifiableSet(constraintDescriptors);
+ }
+
+ /**
+ * Get the mutable {@link ConstraintDescriptor} {@link Set}.
+ *
+ * @return Set of {@link ConstraintDescriptor}
+ */
+ protected Set<ConstraintDescriptor<?>> getMutableConstraintDescriptors() {
+ return constraintDescriptors;
+ }
+
+ /**
+ * {@inheritDoc} return true if at least one constraint declaration is
+ * present on the element.
+ */
+ public boolean hasConstraints() {
+ return !getConstraintDescriptors().isEmpty();
+ }
+
+ /**
+ * Set the constraintDescriptors for this element.
+ *
+ * @param constraintDescriptors
+ * to set
+ */
+ public void setConstraintDescriptors(Set<ConstraintDescriptor<?>> constraintDescriptors) {
+ this.constraintDescriptors = constraintDescriptors;
+ }
+
+ /**
+ * Get the model {@link MetaBean} used.
+ *
+ * @return MetaBean
+ */
+ public MetaBean getMetaBean() {
+ return metaBean;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java
new file mode 100644
index 0000000..358bbab
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+/**
+ * Class that stores the needed properties to avoid circular paths when
+ * validating an object graph.
+ * <p>
+ * These properties are:
+ * <ul>
+ * <li>The ref of the bean to which the validation would be applied.</li>
+ * <li>The current group being validated.</li>
+ * </ul>
+ *
+ * FIXME: Owner is currently not used in identity checking, and probably
+ * never will be. So it is likely to be deleted.
+ *
+ * @author Carlos Vara
+ */
+public class GraphBeanIdentity {
+
+ private final Object bean;
+ private final Class<?> group;
+ private final Class<?> owner;
+
+ /**
+ * Create a new GraphBeanIdentity instance.
+ * @param bean
+ * @param group
+ * @param owner
+ */
+ public GraphBeanIdentity(Object bean, Class<?> group, Class<?> owner) {
+ this.bean = bean;
+ this.group = group;
+ this.owner = owner;
+ }
+
+ /**
+ * Get the bean.
+ * @return Object
+ */
+ public Object getBean() {
+ return bean;
+ }
+
+ /**
+ * Get the group being validated.
+ * @return Class
+ */
+ public Class<?> getGroup() {
+ return group;
+ }
+
+ /**
+ * Get the owning class
+ * @return
+ */
+ public Class<?> getOwner() {
+ return owner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (!(obj instanceof GraphBeanIdentity)) {
+ return false;
+ }
+
+ GraphBeanIdentity other = (GraphBeanIdentity) obj;
+
+ // Bean ref must be the same
+ if (this.bean != other.bean) {
+ return false;
+ }
+
+ // Group ref must be the same
+ if (this.group != other.group) {
+ return false;
+ }
+
+// // Owner ref must be the same
+// if (this.owner != other.owner) {
+// return false;
+// }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((this.bean == null) ? 0 : this.bean.hashCode());
+ result = prime * result
+ + ((this.group == null) ? 0 : this.group.hashCode());
+// result = prime * result
+// + ((this.owner == null) ? 0 : this.owner.hashCode());
+ return result;
+ }
+
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java
new file mode 100644
index 0000000..808a51c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.ValidationContext;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+
+/**
+ * Description: JSR-303 {@link ValidationContext} extension. <br/>
+ */
+public interface GroupValidationContext<T>
+ extends ValidationContext<ConstraintValidationListener<T>> {
+
+ /**
+ * Get the groups of this {@link GroupValidationContext}.
+ * @return the groups in their sequence for validation
+ */
+ Groups getGroups();
+
+ /**
+ * Set the current {@link Group}.
+ * @param group to set
+ */
+ void setCurrentGroup(Group group);
+
+ /**
+ * Get the current {@link Group}.
+ * @return Group
+ */
+ Group getCurrentGroup();
+
+ /**
+ * Get the property path.
+ * @return {@link PathImpl}
+ */
+ PathImpl getPropertyPath();
+
+ /**
+ * Get the root {@link MetaBean}.
+ * @return {@link MetaBean}
+ */
+ MetaBean getRootMetaBean();
+
+ /**
+ * Set the {@link ConstraintValidation}.
+ * @param constraint to set
+ */
+ void setConstraintValidation(ConstraintValidation<?> constraint);
+
+ /**
+ * Get the {@link ConstraintValidation}.
+ * @return {@link ConstraintValidation}
+ */
+ ConstraintValidation<?> getConstraintValidation();
+
+ /**
+ * Get the value being validated.
+ * @return Object
+ */
+ Object getValidatedValue();
+
+ /**
+ * Set a fixed value for the context.
+ * @param value to set
+ */
+ void setFixedValue(Object value);
+
+ /**
+ * Get the message resolver.
+ * @return {@link MessageInterpolator}
+ */
+ MessageInterpolator getMessageResolver();
+
+ /**
+ * Get the {@link TraversableResolver}.
+ * @return {@link TraversableResolver}
+ */
+ TraversableResolver getTraversableResolver();
+
+ /**
+ * Accumulate a validated constraint.
+ * @param constraint
+ * @return true when the constraint for the object in this path was not
+ * already validated in this context
+ */
+ boolean collectValidated(ConstraintValidator<?, ?> constraint);
+
+ /**
+ * Get the current owning class.
+ * @return Class
+ */
+ Class<?> getCurrentOwner();
+
+ /**
+ * Set the current owning class.
+ * @param currentOwner to set
+ */
+ void setCurrentOwner(Class<?> currentOwner);
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java
new file mode 100644
index 0000000..3bb5090
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.BeanValidationContext;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.resolver.CachingTraversableResolver;
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: instance per validation process, not thread-safe<br/>
+ */
+final class GroupValidationContextImpl<T> extends BeanValidationContext<ConstraintValidationListener<T>> implements
+ GroupValidationContext<T>, MessageInterpolator.Context {
+
+ private final MessageInterpolator messageResolver;
+ private final PathImpl path;
+ private final MetaBean rootMetaBean;
+
+ /**
+ * the groups in the sequence of validation to take place
+ */
+ private Groups groups;
+ /**
+ * the current group during the validation process
+ */
+ private Group currentGroup;
+
+ private Class<?> currentOwner;
+
+ /**
+ * contains the validation constraints that have already been processed
+ * during this validation routine (as part of a previous group match)
+ */
+ private HashSet<ConstraintValidatorIdentity> validatedConstraints = new HashSet<ConstraintValidatorIdentity>();
+
+ private ConstraintValidation<?> constraintValidation;
+ private final TraversableResolver traversableResolver;
+
+ /**
+ * Create a new GroupValidationContextImpl instance.
+ *
+ * @param listener
+ * @param aMessageResolver
+ * @param traversableResolver
+ * @param rootMetaBean
+ */
+ public GroupValidationContextImpl(ConstraintValidationListener<T> listener, MessageInterpolator aMessageResolver,
+ TraversableResolver traversableResolver, MetaBean rootMetaBean) {
+ // inherited variable 'validatedObjects' is of type:
+ // HashMap<GraphBeanIdentity, Set<PathImpl>> in this class
+ super(listener, new HashMap<GraphBeanIdentity, Set<PathImpl>>());
+ this.messageResolver = aMessageResolver;
+ this.traversableResolver = CachingTraversableResolver.cacheFor(traversableResolver);
+ this.rootMetaBean = rootMetaBean;
+ this.path = PathImpl.create(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setCurrentIndex(Integer index) {
+ NodeImpl leaf = path.getLeafNode();
+ if (leaf.getName() == null) {
+ leaf.setIndex(index);
+ } else {
+ path.addNode(NodeImpl.atIndex(index));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setCurrentKey(Object key) {
+ NodeImpl leaf = path.getLeafNode();
+ if (leaf.getName() == null) {
+ leaf.setKey(key);
+ } else {
+ path.addNode(NodeImpl.atKey(key));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void moveDown(MetaProperty prop, AccessStrategy access) {
+ path.addProperty(prop.getName());
+ super.moveDown(prop, access);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void moveUp(Object bean, MetaBean metaBean) {
+ NodeImpl leaf = path.getLeafNode();
+ if (leaf.isInIterable() && leaf.getName() != null) {
+ leaf.setName(null);
+ } else {
+ path.removeLeafNode();
+ }
+ super.moveUp(bean, metaBean); // call super!
+ }
+
+ /**
+ * {@inheritDoc} Here, state equates to bean identity + group.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean collectValidated() {
+
+ // Combination of bean+group+owner (owner is currently ignored)
+ GraphBeanIdentity gbi = new GraphBeanIdentity(getBean(), getCurrentGroup().getGroup(), getCurrentOwner());
+
+ Set<PathImpl> validatedPathsForGBI = (Set<PathImpl>) validatedObjects.get(gbi);
+ if (validatedPathsForGBI == null) {
+ validatedPathsForGBI = new HashSet<PathImpl>();
+ validatedObjects.put(gbi, validatedPathsForGBI);
+ }
+
+ // If any of the paths is a subpath of the current path, there is a
+ // circular dependency, so return false
+ for (PathImpl validatedPath : validatedPathsForGBI) {
+ if (path.isSubPathOf(validatedPath)) {
+ return false;
+ }
+ }
+
+ // Else, add the currentPath to the set of validatedPaths
+ validatedPathsForGBI.add(PathImpl.copy(path));
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean collectValidated(ConstraintValidator<?, ?> constraint) {
+ ConstraintValidatorIdentity cvi = new ConstraintValidatorIdentity(getBean(), getPropertyPath(), constraint);
+ return this.validatedConstraints.add(cvi);
+ }
+
+ /**
+ * Reset the validated constraints.
+ */
+ public void resetValidatedConstraints() {
+ validatedConstraints.clear();
+ }
+
+ /**
+ * {@inheritDoc} If an associated object is validated, add the association
+ * field or JavaBeans property name and a dot ('.') as a prefix to the
+ * previous rules. uses prop[index] in property path for elements in
+ * to-many-relationships.
+ *
+ * @return the path in dot notation
+ */
+ public PathImpl getPropertyPath() {
+ PathImpl currentPath = PathImpl.copy(path);
+ if (getMetaProperty() != null) {
+ currentPath.addProperty(getMetaProperty().getName());
+ }
+ return currentPath;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaBean getRootMetaBean() {
+ return rootMetaBean;
+ }
+
+ /**
+ * Set the Groups.
+ *
+ * @param groups
+ */
+ public void setGroups(Groups groups) {
+ this.groups = groups;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Groups getGroups() {
+ return groups;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Group getCurrentGroup() {
+ return currentGroup;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCurrentGroup(Group currentGroup) {
+ this.currentGroup = currentGroup;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setConstraintValidation(ConstraintValidation<?> constraint) {
+ constraintValidation = constraint;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidation<?> getConstraintValidation() {
+ return constraintValidation;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return constraintValidation;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getValidatedValue() {
+ if (getMetaProperty() != null) {
+ return getPropertyValue(constraintValidation.getAccess());
+ } else {
+ return getBean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MessageInterpolator getMessageResolver() {
+ return messageResolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TraversableResolver getTraversableResolver() {
+ return traversableResolver;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<?> getCurrentOwner() {
+ return this.currentOwner;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCurrentOwner(Class<?> currentOwner) {
+ this.currentOwner = currentOwner;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/IncompatiblePropertyValueException.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/IncompatiblePropertyValueException.java
new file mode 100644
index 0000000..8c67751
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/IncompatiblePropertyValueException.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.ValidationException;
+
+/**
+ * Internal exception thrown when trying to validate a value for a property for which it is not assignment-compatible.
+ *
+ * @version $Rev: 1031833 $ $Date: 2010-11-05 16:53:03 -0500 (Fri, 05 Nov 2010) $
+ *
+ * @author Matt Benson
+ */
+public class IncompatiblePropertyValueException extends ValidationException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new {@link IncompatiblePropertyValueException} instance.
+ *
+ * @param message
+ */
+ public IncompatiblePropertyValueException(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new IncompatiblePropertyValueException instance.
+ */
+ public IncompatiblePropertyValueException() {
+ super();
+ }
+
+ /**
+ * Create a new IncompatiblePropertyValueException instance.
+ *
+ * @param message
+ * @param cause
+ */
+ public IncompatiblePropertyValueException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Create a new IncompatiblePropertyValueException instance.
+ *
+ * @param cause
+ */
+ public IncompatiblePropertyValueException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303Features.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303Features.java
new file mode 100644
index 0000000..4bd6990
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303Features.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.model.Features;
+
+/**
+ * Description: Contains MetaBean feature keys of additional features used in the implementation
+ * of JSR303<br/>
+ *
+ * @see org.apache.bval.model.FeaturesCapable
+ * @see org.apache.bval.model.Features
+ */
+public interface Jsr303Features {
+ /**
+ * JSR303 Property features
+ */
+ interface Property extends Features.Property {
+ /** INFO: cached PropertyDescriptorImpl of the property */
+ String PropertyDescriptor = "PropertyDescriptor";
+ /**
+ * INFO: Class[] with the groups to validate a REF_CASCADE
+ */
+ String REF_GROUPS = "refGroups";
+ }
+
+ /**
+ * JSR303 bean features
+ */
+ interface Bean extends Features.Bean {
+ /**
+ * INFO: List of Group(Class) for {@link javax.validation.GroupSequence#value()}
+ * (redefined default group)
+ **/
+ String GROUP_SEQUENCE = "GroupSequence";
+
+// INFO: cached sorted Array with ValidationEntries
+// String VALIDATION_SEQUENCE = "ValidationSequence";
+
+ /**
+ * INFO: cached BeanDescriptorImpl of the bean
+ */
+ String BEAN_DESCRIPTOR = "BeanDescriptor";
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
new file mode 100644
index 0000000..296e8b1
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.validation.Constraint;
+import javax.validation.GroupDefinitionException;
+import javax.validation.GroupSequence;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.jsr303.xml.MetaConstraint;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+
+/**
+ * Description: process the class annotations for JSR303 constraint validations to build the MetaBean with information
+ * from annotations and JSR303 constraint mappings (defined in xml)<br/>
+ */
+public class Jsr303MetaBeanFactory implements MetaBeanFactory {
+ /** Shared log instance */
+ // of dubious utility as it's static :/
+ protected static final Logger log = Logger.getLogger(Jsr303MetaBeanFactory.class.getName());
+
+ /** {@link ApacheFactoryContext} used */
+ protected final ApacheFactoryContext factoryContext;
+
+ /**
+ * {@link AnnotationProcessor} used.
+ */
+ protected AnnotationProcessor annotationProcessor;
+
+ /**
+ * Create a new Jsr303MetaBeanFactory instance.
+ *
+ * @param factoryContext
+ */
+ public Jsr303MetaBeanFactory(ApacheFactoryContext factoryContext) {
+ this.factoryContext = factoryContext;
+ this.annotationProcessor = new AnnotationProcessor(factoryContext);
+ }
+
+ /**
+ * {@inheritDoc} Add the validation features to the metabean that come from JSR303 annotations in the beanClass.
+ */
+ public void buildMetaBean(MetaBean metabean) {
+ try {
+ final Class<?> beanClass = metabean.getBeanClass();
+ processGroupSequence(beanClass, metabean);
+
+ // process class, superclasses and interfaces
+ List<Class<?>> classSequence = new ArrayList<Class<?>>();
+ ClassHelper.fillFullClassHierarchyAsList(classSequence, beanClass);
+
+ // start with superclasses and go down the hierarchy so that
+ // the child classes are processed last to have the chance to
+ // overwrite some declarations
+ // of their superclasses and that they see what they inherit at the
+ // time of processing
+ for (int i = classSequence.size() - 1; i >= 0; i--) {
+ Class<?> eachClass = classSequence.get(i);
+ processClass(eachClass, metabean);
+ processGroupSequence(eachClass, metabean, "{GroupSequence:" + eachClass.getCanonicalName() + "}");
+ }
+
+ } catch (IllegalAccessException e) {
+ throw new IllegalArgumentException(e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalArgumentException(e.getTargetException());
+ }
+ }
+
+ /**
+ * Process class annotations, field and method annotations.
+ *
+ * @param beanClass
+ * @param metabean
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ private void processClass(Class<?> beanClass, MetaBean metabean) throws IllegalAccessException,
+ InvocationTargetException {
+
+ // if NOT ignore class level annotations
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(beanClass)) {
+ annotationProcessor.processAnnotations(null, beanClass, beanClass, null, new AppendValidationToMeta(
+ metabean));
+ }
+
+ final Field[] fields = doPrivileged(SecureActions.getDeclaredFields(beanClass));
+ for (Field field : fields) {
+ MetaProperty metaProperty = metabean.getProperty(field.getName());
+ // create a property for those fields for which there is not yet a
+ // MetaProperty
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(field)) {
+ AccessStrategy access = new FieldAccess(field);
+ boolean create = metaProperty == null;
+ if (create) {
+ metaProperty = addMetaProperty(metabean, access);
+ }
+ if (!annotationProcessor.processAnnotations(metaProperty, beanClass, field, access,
+ new AppendValidationToMeta(metaProperty)) && create) {
+ metabean.putProperty(metaProperty.getName(), null);
+ }
+ }
+ }
+ final Method[] methods = doPrivileged(SecureActions.getDeclaredMethods(beanClass));
+ for (Method method : methods) {
+ String propName = null;
+ if (method.getParameterTypes().length == 0) {
+ propName = MethodAccess.getPropertyName(method);
+ }
+ if (propName != null) {
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+ AccessStrategy access = new MethodAccess(propName, method);
+ MetaProperty metaProperty = metabean.getProperty(propName);
+ boolean create = metaProperty == null;
+ // create a property for those methods for which there is
+ // not yet a MetaProperty
+ if (create) {
+ metaProperty = addMetaProperty(metabean, access);
+ }
+ if (!annotationProcessor.processAnnotations(metaProperty, beanClass, method, access,
+ new AppendValidationToMeta(metaProperty)) && create) {
+ metabean.putProperty(propName, null);
+ }
+ }
+ } else if (hasValidationConstraintsDefined(method)) {
+ throw new ValidationException("Property " + method.getName() + " does not follow javabean conventions.");
+ }
+ }
+
+ addXmlConstraints(beanClass, metabean);
+ }
+
+ /**
+ * Learn whether a given Method has validation constraints defined via JSR303 annotations.
+ *
+ * @param method
+ * @return <code>true</code> if constraints detected
+ */
+ protected boolean hasValidationConstraintsDefined(Method method) {
+ for (Annotation annot : method.getDeclaredAnnotations()) {
+ if (hasValidationConstraintsDefined(annot)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasValidationConstraintsDefined(Annotation annot) {
+ // If it is annotated with @Constraint
+ if (annot.annotationType().getAnnotation(Constraint.class) != null) {
+ return true;
+ }
+
+ // Check whether it is a multivalued constraint:
+ if (ConstraintAnnotationAttributes.VALUE.isDeclaredOn(annot.annotationType())) {
+ Annotation[] children = ConstraintAnnotationAttributes.VALUE.getValue(annot);
+ if (children != null) {
+ for (Annotation child : children) {
+ if (hasValidationConstraintsDefined(child)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Add cascade validation and constraints from xml mappings
+ *
+ * @param beanClass
+ * @param metabean
+ * @throws IllegalAccessException
+ * @throws InvocationTargetException
+ */
+ private void addXmlConstraints(Class<?> beanClass, MetaBean metabean) throws IllegalAccessException,
+ InvocationTargetException {
+ for (MetaConstraint<?, ? extends Annotation> meta : factoryContext.getFactory().getMetaConstraints(beanClass)) {
+ MetaProperty metaProperty;
+ AccessStrategy access = meta.getAccessStrategy();
+ boolean create = false;
+ if (access == null) { // class level
+ metaProperty = null;
+ } else { // property level
+ metaProperty = metabean.getProperty(access.getPropertyName());
+ create = metaProperty == null;
+ if (create) {
+ metaProperty = addMetaProperty(metabean, access);
+ }
+ }
+ if (!annotationProcessor.processAnnotation(meta.getAnnotation(), metaProperty, beanClass,
+ meta.getAccessStrategy(), new AppendValidationToMeta(metaProperty == null ? metabean : metaProperty))
+ && create) {
+ metabean.putProperty(access.getPropertyName(), null);
+ }
+ }
+ for (AccessStrategy access : factoryContext.getFactory().getValidAccesses(beanClass)) {
+ MetaProperty metaProperty = metabean.getProperty(access.getPropertyName());
+ boolean create = metaProperty == null;
+ if (create) {
+ metaProperty = addMetaProperty(metabean, access);
+ }
+ if (!annotationProcessor.addAccessStrategy(metaProperty, access) && create) {
+ metabean.putProperty(access.getPropertyName(), null);
+ }
+ }
+ }
+
+ private void processGroupSequence(Class<?> beanClass, MetaBean metabean) {
+ processGroupSequence(beanClass, metabean, Jsr303Features.Bean.GROUP_SEQUENCE);
+ }
+
+ private void processGroupSequence(Class<?> beanClass, MetaBean metabean, String key) {
+ GroupSequence annotation = beanClass.getAnnotation(GroupSequence.class);
+ List<Group> groupSeq = metabean.getFeature(key);
+ if (groupSeq == null) {
+ groupSeq = new ArrayList<Group>(annotation == null ? 1 : annotation.value().length);
+ metabean.putFeature(key, groupSeq);
+ }
+ Class<?>[] groupClasses = factoryContext.getFactory().getDefaultSequence(beanClass);
+ if (groupClasses == null || groupClasses.length == 0) {
+ if (annotation == null) {
+ groupSeq.add(Group.DEFAULT);
+ return;
+ } else {
+ groupClasses = annotation.value();
+ }
+ }
+ boolean containsDefault = false;
+ for (Class<?> groupClass : groupClasses) {
+ if (groupClass.getName().equals(beanClass.getName())) {
+ groupSeq.add(Group.DEFAULT);
+ containsDefault = true;
+ } else if (groupClass.getName().equals(Default.class.getName())) {
+ throw new GroupDefinitionException("'Default.class' must not appear in @GroupSequence! Use '"
+ + beanClass.getSimpleName() + ".class' instead.");
+ } else {
+ groupSeq.add(new Group(groupClass));
+ }
+ }
+ if (!containsDefault) {
+ throw new GroupDefinitionException("Redefined default group sequence must contain " + beanClass.getName());
+ }
+ log.log(Level.FINEST, String.format("Default group sequence for bean %s is: %s", beanClass.getName(), groupSeq));
+ }
+
+ /**
+ * Add a {@link MetaProperty} to a {@link MetaBean}.
+ * @param parentMetaBean
+ * @param access
+ * @return the created {@link MetaProperty}
+ */
+ public static MetaProperty addMetaProperty(MetaBean parentMetaBean, AccessStrategy access) {
+ final MetaProperty result = new MetaProperty();
+ final String name = access.getPropertyName();
+ result.setName(name);
+ result.setType(access.getJavaType());
+ parentMetaBean.putProperty(name, result);
+ return result;
+ }
+
+
+
+
+ /**
+ * Perform action with AccessController.doPrivileged() if a security manager is installed.
+ *
+ * @param action
+ * the action to run
+ * @return
+ * result of the action
+ */
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/PropertyDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/PropertyDescriptorImpl.java
new file mode 100644
index 0000000..20197e4
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/PropertyDescriptorImpl.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+
+import javax.validation.metadata.PropertyDescriptor;
+
+/**
+ * Description: {@link PropertyDescriptor} implementation.<br/>
+ */
+class PropertyDescriptorImpl extends ElementDescriptorImpl implements PropertyDescriptor {
+ private boolean cascaded;
+ private String propertyPath;
+
+ /**
+ * Create a new PropertyDescriptorImpl instance.
+ *
+ * @param property
+ */
+ PropertyDescriptorImpl(MetaProperty property) {
+ super(property.getParentMetaBean(), property.getTypeClass(), property.getValidations());
+ setCascaded(property.getMetaBean() != null || property.getFeature(Features.Property.REF_CASCADE) != null);
+ setPropertyPath(property.getName());
+ }
+
+ /**
+ * Set whether the referenced property is cascaded.
+ *
+ * @param cascaded
+ */
+ public void setCascaded(boolean cascaded) {
+ this.cascaded = cascaded;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascaded() {
+ return cascaded;
+ }
+
+ /**
+ * Set the referenced property path.
+ *
+ * @param propertyPath
+ */
+ public void setPropertyPath(String propertyPath) {
+ this.propertyPath = propertyPath;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getPropertyName() {
+ return propertyPath;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return "PropertyDescriptorImpl{" + "returnType=" + elementClass + ", propertyPath='" + propertyPath + '\''
+ + '}';
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/UnknownPropertyException.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/UnknownPropertyException.java
new file mode 100644
index 0000000..dabcf6e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/UnknownPropertyException.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import javax.validation.ValidationException;
+
+/**
+ * Internal exception thrown when trying to access a property that doesn't exist
+ * in a bean.
+ *
+ * @version $Rev$ $Date$
+ *
+ * @author Carlos Vara
+ */
+public class UnknownPropertyException extends ValidationException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create a new UnknownPropertyException instance.
+ * @param message
+ */
+ public UnknownPropertyException(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new UnknownPropertyException instance.
+ */
+ public UnknownPropertyException() {
+ super();
+ }
+
+ /**
+ * Create a new UnknownPropertyException instance.
+ * @param message
+ * @param cause
+ */
+ public UnknownPropertyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Create a new UnknownPropertyException instance.
+ * @param cause
+ */
+ public UnknownPropertyException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/AppendValidationToList.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/AppendValidationToList.java
new file mode 100644
index 0000000..602f27c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/AppendValidationToList.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+
+import org.apache.bval.jsr303.AppendValidation;
+import org.apache.bval.jsr303.BaseAppendValidation;
+import org.apache.bval.jsr303.ConstraintValidation;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: {@link AppendValidation} implementation that acts as an intermediate
+ * cache of validations for further processing.<br/>
+ */
+public class AppendValidationToList extends BaseAppendValidation {
+ private final List<ConstraintValidation<?>> validations = new ArrayList<ConstraintValidation<?>>();
+
+ /**
+ * Create a new AppendValidationToList instance.
+ */
+ public AppendValidationToList() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+ validations.add(validation);
+ }
+
+ /**
+ * Get the list of cached validations.
+ * @return {@link List} of {@link ConstraintValidation}
+ */
+ public List<ConstraintValidation<?>> getValidations() {
+ return validations;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptor.java
new file mode 100644
index 0000000..6e273fd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptor.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import javax.validation.metadata.ElementDescriptor;
+import java.util.List;
+
+/**
+ * Description: This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface ConstructorDescriptor extends ElementDescriptor {
+ /**
+ * Get the list of {@link ParameterDescriptor}s.
+ * @return {@link List} of {@link ParameterDescriptor}
+ */
+ List<ParameterDescriptor> getParameterDescriptors(); //index aligned
+
+ /**
+ * Learn whether the referenced constructor should be validated.
+ * @return
+ */
+ boolean isCascaded();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptorImpl.java
new file mode 100644
index 0000000..775c83d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptorImpl.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+
+import org.apache.bval.jsr303.ElementDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: {@link ConstructorDescriptor} implementation.<br/>
+ */
+public class ConstructorDescriptorImpl extends ElementDescriptorImpl
+ implements ConstructorDescriptor, ProcedureDescriptor {
+ private final List<ParameterDescriptor> parameterDescriptors = new ArrayList<ParameterDescriptor>();
+ private boolean cascaded;
+
+ /**
+ * Create a new ConstructorDescriptorImpl instance.
+ * @param metaBean
+ * @param validations
+ */
+ protected ConstructorDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+ super(metaBean, metaBean.getBeanClass(), validations);
+ }
+
+ /**
+ * Create a new ConstructorDescriptorImpl instance.
+ * @param elementClass
+ * @param validations
+ */
+ protected ConstructorDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+ super(elementClass, validations);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascaded() {
+ return cascaded;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCascaded(boolean cascaded) {
+ this.cascaded = cascaded;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<ParameterDescriptor> getParameterDescriptors() //index aligned
+ {
+ return parameterDescriptors;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptor.java
new file mode 100644
index 0000000..5cac52e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptor.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import javax.validation.metadata.BeanDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * Description: Provides method/constructor-related constraint information
+ * for a type. This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface MethodBeanDescriptor extends BeanDescriptor {
+ /**
+ * Get the constraints that apply to a particular method.
+ * @param method
+ * @return {@link MethodDescriptor}
+ */
+ MethodDescriptor getConstraintsForMethod(Method method);
+
+ /**
+ * Get the constraints that apply to a particular constructor.
+ * @param constructor
+ * @return {@link ConstructorDescriptor}
+ */
+ ConstructorDescriptor getConstraintsForConstructor(Constructor<?> constructor);
+
+ /**
+ * Get the set of constrained methods.
+ * @return {@link Set} of {@link MethodDescriptor}
+ */
+ Set<MethodDescriptor> getConstrainedMethods();
+
+ /**
+ * Get the set of constrained constructors.
+ * @return {@link Set} of {@link ConstructorDescriptor}
+ */
+ Set<ConstructorDescriptor> getConstrainedConstructors();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptorImpl.java
new file mode 100644
index 0000000..e8a482d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptorImpl.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import org.apache.bval.jsr303.ApacheFactoryContext;
+import org.apache.bval.jsr303.BeanDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Description: {@link MethodBeanDescriptor} implementation.<br/>
+ */
+class MethodBeanDescriptorImpl extends BeanDescriptorImpl implements MethodBeanDescriptor {
+ private Map<Method, MethodDescriptor> methodConstraints;
+ private Map<Constructor<?>, ConstructorDescriptor> constructorConstraints;
+
+ /**
+ * Create a new MethodBeanDescriptorImpl instance.
+ *
+ * @param factoryContext
+ * @param metaBean
+ */
+ protected MethodBeanDescriptorImpl(ApacheFactoryContext factoryContext, MetaBean metaBean) {
+ super(factoryContext, metaBean);
+ }
+
+ /**
+ * Set the map of method constraints for this bean.
+ *
+ * @param methodConstraints
+ */
+ public void setMethodConstraints(Map<Method, MethodDescriptor> methodConstraints) {
+ this.methodConstraints = methodConstraints;
+ }
+
+ /**
+ * Set the map of constructor constraints for this bean.
+ *
+ * @param constructorConstraints
+ */
+ public void setConstructorConstraints(Map<Constructor<?>, ConstructorDescriptor> constructorConstraints) {
+ this.constructorConstraints = constructorConstraints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MethodDescriptor getConstraintsForMethod(Method method) {
+ return methodConstraints.get(method);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstructorDescriptor getConstraintsForConstructor(Constructor<?> constructor) {
+ return constructorConstraints.get(constructor);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<MethodDescriptor> getConstrainedMethods() {
+ return new HashSet<MethodDescriptor>(methodConstraints.values());
+ }
+
+ /**
+ * Add a {@link MethodDescriptor} to this {@link MethodBeanDescriptorImpl}.
+ *
+ * @param method
+ * @param desc
+ */
+ public void putMethodDescriptor(Method method, MethodDescriptor desc) {
+ methodConstraints.put(method, desc);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set<ConstructorDescriptor> getConstrainedConstructors() {
+ return new HashSet<ConstructorDescriptor>(this.constructorConstraints.values());
+ }
+
+ /**
+ * Add a {@link ConstructorDescriptor} to this
+ * {@link MethodBeanDescriptorImpl}.
+ *
+ * @param cons
+ * @param desc
+ */
+ public void putConstructorDescriptor(Constructor<?> cons, ConstructorDescriptor desc) {
+ constructorConstraints.put(cons, desc);
+ }
+
+ /**
+ * Get the configured method constraints.
+ *
+ * @return {@link Map} of {@link Method} : {@link MethodDescriptor}
+ */
+ public Map<Method, MethodDescriptor> getMethodConstraints() {
+ return methodConstraints;
+ }
+
+ /**
+ * Get the configured constructor constraints.
+ *
+ * @return {@link Map} of {@link Constructor} :
+ * {@link ConstructorDescriptor}
+ */
+ public Map<Constructor<?>, ConstructorDescriptor> getConstructorConstraints() {
+ return constructorConstraints;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptor.java
new file mode 100644
index 0000000..71a4289
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import javax.validation.metadata.ElementDescriptor;
+import java.util.List;
+
+/**
+ * Description: This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface MethodDescriptor extends ElementDescriptor {
+ /**
+ * Get the {@link ParameterDescriptor}s for this {@link MethodDescriptor}.
+ * @return {@link List} of {@link ParameterDescriptor}
+ */
+ List<ParameterDescriptor> getParameterDescriptors(); //index aligned
+
+ /**
+ * Learn whether the referenced method should be validated.
+ * @return boolean
+ */
+ boolean isCascaded();
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptorImpl.java
new file mode 100644
index 0000000..4281414
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptorImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import org.apache.bval.jsr303.ConstraintValidation;
+import org.apache.bval.jsr303.ElementDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Description: {@link MethodDescriptor} implementation.<br/>
+ */
+public class MethodDescriptorImpl extends ElementDescriptorImpl implements MethodDescriptor, ProcedureDescriptor {
+ private final List<ParameterDescriptor> parameterDescriptors = new ArrayList<ParameterDescriptor>();
+ private boolean cascaded;
+
+ /**
+ * Create a new MethodDescriptorImpl instance.
+ *
+ * @param metaBean
+ * @param validations
+ */
+ protected MethodDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+ super(metaBean, metaBean.getClass(), validations);
+ }
+
+ /**
+ * Create a new MethodDescriptorImpl instance.
+ *
+ * @param elementClass
+ * @param validations
+ */
+ protected MethodDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+ super(elementClass, validations);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<ParameterDescriptor> getParameterDescriptors() {
+ // index aligned
+ return parameterDescriptors;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascaded() {
+ return cascaded;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setCascaded(boolean cascaded) {
+ this.cascaded = cascaded;
+ }
+
+ /**
+ * Add the specified validations to this {@link MethodDescriptorImpl}.
+ * @param validations
+ */
+ void addValidations(Collection<ConstraintValidation<?>> validations) {
+ getMutableConstraintDescriptors().addAll(validations);
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidator.java
new file mode 100644
index 0000000..8f35642
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidator.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * Description: Appendix C. Proposal for method-level validation.
+ * This interface contains the APIs added to javax.validation.Validator.
+ * It can be removed as soon as the Validator interface contains these methods.
+ * The extension is not a part of the JSR303 core specification yet, but could
+ * be in a future revision.<br/>
+ * You can access the extension via the use of the Validator.unwrap() method.<br/>
+ */
+public interface MethodValidator extends Validator {
+ /**
+ * Validate each parameter value based on the constraints described on
+ * the parameters of <code>method</code>.
+ *
+ * @param clazz class hosting the method
+ * @param method the method whose parameters are currectly validated
+ * @param parameterValues the parameter values passed to the method for invocation
+ * @param groups groups targeted for validation
+ * @return set of constraint violations
+ * @throws IllegalArgumentException if the method does not belong to <code>T</code>
+ * or if the Object[] does not match the method signature
+ */
+ <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Method method,
+ Object[] parameterValues,
+ Class<?>... groups);
+
+ /**
+ * Validate the parameter value based on the constraints described on
+ * the parameterIndex-th parameter of <code>method</code>.
+ *
+ * @param clazz class hosting the method
+ * @param method the method whose parameters are currectly validated
+ * @param parameterValue the parameter value passed to the parameterIndex-t parameter of method
+ * @param parameterIndex parameter index of the parameter validated in method
+ * @param groups groups targeted for validation
+ * @return set of constraint violations
+ * @throws IllegalArgumentException if the method does not belong to <code>T</code>
+ * or if parameterIndex is out of bound
+ */
+ <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Method method,
+ Object parameterValue,
+ int parameterIndex,
+ Class<?>... groups);
+
+ /**
+ * Validate each parameter value based on the constraints described on
+ * <code>method</code>.
+ *
+ * @param clazz class hosting the method
+ * @param method the method whose result is validated
+ * @param returnedValue the value returned by the method invocation
+ * @param groups groups targeted for validation
+ * @return set of constraint violations
+ * @throws IllegalArgumentException if the method does not belong to <code>T</code>
+ */
+ <T> Set<ConstraintViolation<T>> validateReturnedValue(Class<T> clazz, Method method,
+ Object returnedValue,
+ Class<?>... groups);
+
+ /**
+ * Validate each parameter value based on the constraints described on
+ * the parameters of <code>constructor</code>.
+ *
+ * @param clazz class hosting the constructor
+ * @param constructor the constructor whose parameters are correctly validated
+ * @param parameterValues the parameter values passed to the constructor for invocation
+ * @param groups groups targeted for validation
+ * @return set of constraint violations
+ * @throws IllegalArgumentException if the constructor does not belong to <code>T</code>
+ * or if the Object[] does not match the constructor signature
+ */
+ <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz,
+ Constructor<T> constructor,
+ Object[] parameterValues,
+ Class<?>... groups);
+
+ /**
+ * Validate the parameter value based on the constraints described on
+ * the parameterIndex-th parameter of <code>constructor</code>.
+ *
+ * @param clazz class hosting the constructor
+ * @param constructor the method whose parameters are correctly validated
+ * @param parameterValue the parameter value passed to the
+ * parameterIndex-th parameter of constructor
+ * @param parameterIndex parameter index of the parameter validated in constructor
+ * @param groups groups targeted for validation
+ * @return set of constraint violations
+ * @throws IllegalArgumentException if the constructor does not belong to <code>T</code>
+ * or if prameterIndex is out of bound
+ */
+ <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz,
+ Constructor<T> constructor,
+ Object parameterValue,
+ int parameterIndex,
+ Class<?>... groups);
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorImpl.java
new file mode 100644
index 0000000..06b5b2f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorImpl.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import org.apache.bval.jsr303.ApacheFactoryContext;
+import org.apache.bval.jsr303.BeanDescriptorImpl;
+import org.apache.bval.jsr303.ClassValidator;
+import org.apache.bval.jsr303.ConstraintValidation;
+import org.apache.bval.jsr303.ConstraintValidationListener;
+import org.apache.bval.jsr303.GroupValidationContext;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.util.ValidationHelper;
+
+/**
+ * Description: experimental implementation of method-level-validation <br/>
+ */
+class MethodValidatorImpl extends ClassValidator implements MethodValidator {
+ /**
+ * Create a new MethodValidatorImpl instance.
+ *
+ * @param factoryContext
+ */
+ public MethodValidatorImpl(ApacheFactoryContext factoryContext) {
+ super(factoryContext);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
+ MethodBeanDescriptorImpl descriptor = new MethodBeanDescriptorImpl(factoryContext, metaBean);
+ MethodValidatorMetaBeanFactory factory = new MethodValidatorMetaBeanFactory(factoryContext);
+ factory.buildMethodDescriptor(descriptor);
+ return descriptor;
+ }
+
+ /**
+ * {@inheritDoc} enhancement: method-level-validation not yet completly
+ * implemented
+ *
+ * <pre>
+ * example:
+ * <code>
+ * public @NotNull String saveItem(@Valid @NotNull Item item, @Max(23) BigDecimal
+ * </code>
+ * </pre>
+ *
+ * spec: The constraints declarations evaluated are the constraints hosted
+ * on the parameters of the method or constructor. If @Valid is placed on a
+ * parameter, constraints declared on the object itself are considered.
+ *
+ * @throws IllegalArgumentException
+ * enhancement: if the method does not belong to <code>T</code>
+ * or if the Object[] does not match the method signature
+ */
+ public <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Method method, Object[] parameters,
+ Class<?>... groupArray) {
+ MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+ MethodDescriptorImpl methodDescriptor = (MethodDescriptorImpl) beanDesc.getConstraintsForMethod(method);
+ if (methodDescriptor == null) {
+ throw new ValidationException("Method " + method + " doesn't belong to class " + clazz);
+ }
+ return validateParameters(methodDescriptor.getMetaBean(), methodDescriptor.getParameterDescriptors(),
+ parameters, groupArray);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Method method, Object parameter,
+ int parameterIndex, Class<?>... groupArray) {
+ MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+ MethodDescriptorImpl methodDescriptor = (MethodDescriptorImpl) beanDesc.getConstraintsForMethod(method);
+ if (methodDescriptor == null) {
+ throw new ValidationException("Method " + method + " doesn't belong to class " + clazz);
+ }
+ ParameterDescriptorImpl paramDesc =
+ (ParameterDescriptorImpl) methodDescriptor.getParameterDescriptors().get(parameterIndex);
+ return validateParameter(paramDesc, parameter, groupArray);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Constructor<T> constructor,
+ Object[] parameters, Class<?>... groupArray) {
+ MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+ ConstructorDescriptorImpl constructorDescriptor =
+ (ConstructorDescriptorImpl) beanDesc.getConstraintsForConstructor(constructor);
+ if (constructorDescriptor == null) {
+ throw new ValidationException("Constructor " + constructor + " doesn't belong to class " + clazz);
+ }
+ return validateParameters(constructorDescriptor.getMetaBean(), constructorDescriptor.getParameterDescriptors(),
+ parameters, groupArray);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Constructor<T> constructor,
+ Object parameter, int parameterIndex, Class<?>... groupArray) {
+ MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+ ConstructorDescriptorImpl constructorDescriptor =
+ (ConstructorDescriptorImpl) beanDesc.getConstraintsForConstructor(constructor);
+ if (constructorDescriptor == null) {
+ throw new ValidationException("Constructor " + constructor + " doesn't belong to class " + clazz);
+ }
+ ParameterDescriptorImpl paramDesc =
+ (ParameterDescriptorImpl) constructorDescriptor.getParameterDescriptors().get(parameterIndex);
+ return validateParameter(paramDesc, parameter, groupArray);
+ }
+
+ /**
+ * {@inheritDoc} If @Valid is placed on the method, the constraints declared
+ * on the object itself are considered.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> Set<ConstraintViolation<T>> validateReturnedValue(Class<T> clazz, Method method, Object returnedValue,
+ Class<?>... groupArray) {
+ MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+ MethodDescriptorImpl methodDescriptor = (MethodDescriptorImpl) beanDesc.getConstraintsForMethod(method);
+ if (methodDescriptor == null) {
+ throw new ValidationException("Method " + method + " doesn't belong to class " + clazz);
+ }
+ final GroupValidationContext<Object> context =
+ createContext(methodDescriptor.getMetaBean(), returnedValue, null, groupArray);
+ validateReturnedValueInContext(context, methodDescriptor);
+ ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+ return result.getConstraintViolations();
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> Set<ConstraintViolation<T>> validateParameters(MetaBean metaBean,
+ List<ParameterDescriptor> paramDescriptors, Object[] parameters, Class<?>... groupArray) {
+ if (parameters == null)
+ throw new IllegalArgumentException("cannot validate null");
+ if (parameters.length > 0) {
+ try {
+ GroupValidationContext<ConstraintValidationListener<Object[]>> context =
+ createContext(metaBean, null, null, groupArray);
+ for (int i = 0; i < parameters.length; i++) {
+ ParameterDescriptorImpl paramDesc = (ParameterDescriptorImpl) paramDescriptors.get(i);
+ context.setBean(parameters[i]);
+ validateParameterInContext(context, paramDesc);
+ }
+ ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+ return result.getConstraintViolations();
+ } catch (RuntimeException ex) {
+ throw unrecoverableValidationError(ex, parameters);
+ }
+ } else {
+ return Collections.<ConstraintViolation<T>> emptySet();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> Set<ConstraintViolation<T>> validateParameter(ParameterDescriptorImpl paramDesc, Object parameter,
+ Class<?>... groupArray) {
+ try {
+ final GroupValidationContext<Object> context =
+ createContext(paramDesc.getMetaBean(), parameter, null, groupArray);
+ final ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+ validateParameterInContext(context, paramDesc);
+ return result.getConstraintViolations();
+ } catch (RuntimeException ex) {
+ throw unrecoverableValidationError(ex, parameter);
+ }
+ }
+
+ /**
+ * validate constraints hosted on parameters of a method
+ */
+ private <T> void validateParameterInContext(GroupValidationContext<T> context, ParameterDescriptorImpl paramDesc) {
+
+ final Groups groups = context.getGroups();
+
+ for (ConstraintDescriptor<?> consDesc : paramDesc.getConstraintDescriptors()) {
+ ConstraintValidation<?> validation = (ConstraintValidation<?>) consDesc;
+ // 1. process groups
+ for (Group current : groups.getGroups()) {
+ context.setCurrentGroup(current);
+ validation.validate(context);
+ }
+ // 2. process sequences
+ for (List<Group> eachSeq : groups.getSequences()) {
+ for (Group current : eachSeq) {
+ context.setCurrentGroup(current);
+ validation.validate(context);
+ /**
+ * if one of the group process in the sequence leads to one
+ * or more validation failure, the groups following in the
+ * sequence must not be processed
+ */
+ if (!context.getListener().isEmpty())
+ break;
+ }
+ }
+ }
+ if (paramDesc.isCascaded() && context.getValidatedValue() != null) {
+ context
+ .setMetaBean(factoryContext.getMetaBeanFinder().findForClass(context.getValidatedValue().getClass()));
+ // 1. process groups
+ for (Group current : groups.getGroups()) {
+ context.setCurrentGroup(current);
+ ValidationHelper
+ .validateContext(context, new Jsr303ValidationCallback(context), isTreatMapsLikeBeans());
+ }
+ // 2. process sequences
+ for (List<Group> eachSeq : groups.getSequences()) {
+ for (Group current : eachSeq) {
+ context.setCurrentGroup(current);
+ ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context),
+ isTreatMapsLikeBeans());
+ /**
+ * if one of the group process in the sequence leads to one
+ * or more validation failure, the groups following in the
+ * sequence must not be processed
+ */
+ if (!context.getListener().isEmpty())
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * validate constraints hosted on parameters of a method
+ */
+ private <T> void validateReturnedValueInContext(GroupValidationContext<T> context,
+ MethodDescriptorImpl methodDescriptor) {
+
+ final Groups groups = context.getGroups();
+
+ for (ConstraintDescriptor<?> consDesc : methodDescriptor.getConstraintDescriptors()) {
+ ConstraintValidation<?> validation = (ConstraintValidation<?>) consDesc;
+ // 1. process groups
+ for (Group current : groups.getGroups()) {
+ context.setCurrentGroup(current);
+ validation.validate(context);
+ }
+ // 2. process sequences
+ for (List<Group> eachSeq : groups.getSequences()) {
+ for (Group current : eachSeq) {
+ context.setCurrentGroup(current);
+ validation.validate(context);
+ /**
+ * if one of the group process in the sequence leads to one
+ * or more validation failure, the groups following in the
+ * sequence must not be processed
+ */
+ if (!context.getListener().isEmpty())
+ break;
+ }
+ }
+ }
+ if (methodDescriptor.isCascaded() && context.getValidatedValue() != null) {
+ context
+ .setMetaBean(factoryContext.getMetaBeanFinder().findForClass(context.getValidatedValue().getClass()));
+ // 1. process groups
+ for (Group current : groups.getGroups()) {
+ context.setCurrentGroup(current);
+ ValidationHelper
+ .validateContext(context, new Jsr303ValidationCallback(context), isTreatMapsLikeBeans());
+ }
+ // 2. process sequences
+ for (List<Group> eachSeq : groups.getSequences()) {
+ for (Group current : eachSeq) {
+ context.setCurrentGroup(current);
+ ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context),
+ isTreatMapsLikeBeans());
+ /**
+ * if one of the group process in the sequence leads to one
+ * or more validation failure, the groups following in the
+ * sequence must not be processed
+ */
+ if (!context.getListener().isEmpty())
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorMetaBeanFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorMetaBeanFactory.java
new file mode 100644
index 0000000..6fbb2b3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorMetaBeanFactory.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import org.apache.bval.Validate;
+import org.apache.bval.jsr303.ApacheFactoryContext;
+import org.apache.bval.jsr303.AppendValidation;
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr303.Jsr303MetaBeanFactory;
+import org.apache.bval.model.Validation;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ClassUtils;
+
+import javax.validation.Constraint;
+import javax.validation.Valid;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * Description: extension to validate parameters/return values of
+ * methods/constructors.<br/>
+ */
+// TODO RSt - move. this is an optional module: move the whole package. core
+// code has no dependencies on it
+public class MethodValidatorMetaBeanFactory extends Jsr303MetaBeanFactory {
+ /**
+ * Create a new MethodValidatorMetaBeanFactory instance.
+ *
+ * @param factoryContext
+ */
+ public MethodValidatorMetaBeanFactory(ApacheFactoryContext factoryContext) {
+ super(factoryContext);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected boolean hasValidationConstraintsDefined(Method method) {
+ return false;
+ }
+
+ /**
+ * Finish building the specified {@link MethodBeanDescriptorImpl}.
+ *
+ * @param descriptor
+ */
+ public void buildMethodDescriptor(MethodBeanDescriptorImpl descriptor) {
+ try {
+ buildMethodConstraints(descriptor);
+ buildConstructorConstraints(descriptor);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e.getMessage(), e);
+ }
+ }
+
+ private void buildConstructorConstraints(MethodBeanDescriptorImpl beanDesc) throws InvocationTargetException,
+ IllegalAccessException {
+ beanDesc.setConstructorConstraints(new HashMap<Constructor<?>, ConstructorDescriptor>());
+
+ for (Constructor<?> cons : beanDesc.getMetaBean().getBeanClass().getDeclaredConstructors()) {
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(cons)) {
+
+ ConstructorDescriptorImpl consDesc =
+ new ConstructorDescriptorImpl(beanDesc.getMetaBean(), new Validation[0]);
+ beanDesc.putConstructorDescriptor(cons, consDesc);
+
+ Annotation[][] paramsAnnos = cons.getParameterAnnotations();
+ int idx = 0;
+ for (Annotation[] paramAnnos : paramsAnnos) {
+ ParameterAccess access = new ParameterAccess(cons.getParameterTypes()[idx], idx);
+ processAnnotations(consDesc, paramAnnos, access, idx);
+ idx++;
+ }
+ }
+ }
+ }
+
+ private void buildMethodConstraints(MethodBeanDescriptorImpl beanDesc) throws InvocationTargetException,
+ IllegalAccessException {
+ beanDesc.setMethodConstraints(new HashMap<Method, MethodDescriptor>());
+
+ for (Method method : beanDesc.getMetaBean().getBeanClass().getDeclaredMethods()) {
+ if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+
+ MethodDescriptorImpl methodDesc = new MethodDescriptorImpl(beanDesc.getMetaBean(), new Validation[0]);
+ beanDesc.putMethodDescriptor(method, methodDesc);
+
+ // return value validations
+ AppendValidationToList validations = new AppendValidationToList();
+ ReturnAccess returnAccess = new ReturnAccess(method.getReturnType());
+ for (Annotation anno : method.getAnnotations()) {
+ if (anno instanceof Valid || anno instanceof Validate) {
+ methodDesc.setCascaded(true);
+ } else {
+ processAnnotation(anno, methodDesc, returnAccess, validations);
+ }
+ }
+ methodDesc.addValidations(validations.getValidations());
+
+ // parameter validations
+ Annotation[][] paramsAnnos = method.getParameterAnnotations();
+ int idx = 0;
+ for (Annotation[] paramAnnos : paramsAnnos) {
+ ParameterAccess access = new ParameterAccess(method.getParameterTypes()[idx], idx);
+ processAnnotations(methodDesc, paramAnnos, access, idx);
+ idx++;
+ }
+ }
+ }
+ }
+
+ private void processAnnotations(ProcedureDescriptor methodDesc, Annotation[] paramAnnos, AccessStrategy access,
+ int idx) throws InvocationTargetException, IllegalAccessException {
+ AppendValidationToList validations = new AppendValidationToList();
+ boolean cascaded = false;
+ for (Annotation anno : paramAnnos) {
+ if (anno instanceof Valid || anno instanceof Validate) {
+ cascaded = true;
+ } else {
+ processAnnotation(anno, methodDesc, access, validations);
+ }
+ }
+ ParameterDescriptorImpl paramDesc =
+ new ParameterDescriptorImpl(methodDesc.getMetaBean(), validations.getValidations().toArray(
+ new Validation[validations.getValidations().size()]));
+ paramDesc.setIndex(idx);
+ paramDesc.setCascaded(cascaded);
+ methodDesc.getParameterDescriptors().add(paramDesc);
+ }
+
+ private <A extends Annotation> void processAnnotation(A annotation, ProcedureDescriptor desc,
+ AccessStrategy access, AppendValidation validations) throws InvocationTargetException, IllegalAccessException {
+
+ if (annotation instanceof Valid || annotation instanceof Validate) {
+ desc.setCascaded(true);
+ } else {
+ Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+ if (vcAnno != null) {
+ annotationProcessor.processAnnotation(annotation, null,
+ ClassUtils.primitiveToWrapper((Class<?>) access.getJavaType()), access, validations);
+ } else {
+ /**
+ * Multi-valued constraints
+ */
+ if (ConstraintAnnotationAttributes.VALUE.isDeclaredOn(annotation.annotationType())) {
+ Annotation[] children = ConstraintAnnotationAttributes.VALUE.getValue(annotation);
+ if (children != null) {
+ for (Annotation child : children) {
+ processAnnotation(child, desc, access, validations); // recursion
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterAccess.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterAccess.java
new file mode 100644
index 0000000..a0f5bc0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterAccess.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303.extensions;
+
+import org.apache.bval.util.AccessStrategy;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+
+
+/**
+ * Implementation of {@link AccessStrategy} for method parameters.
+ *
+ * @author Carlos Vara
+ */
+public class ParameterAccess extends AccessStrategy {
+
+ private Type paramType;
+ private int paramIdx;
+
+ /**
+ * Create a new ParameterAccess instance.
+ * @param paramType
+ * @param paramIdx
+ */
+ public ParameterAccess(Type paramType, int paramIdx ) {
+ this.paramType = paramType;
+ this.paramIdx = paramIdx;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(Object instance) {
+ throw new UnsupportedOperationException("Obtaining a parameter value not yet implemented");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ElementType getElementType() {
+ return ElementType.PARAMETER;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getJavaType() {
+ return this.paramType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPropertyName() {
+ return "" + paramIdx;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptor.java
new file mode 100644
index 0000000..97f5752
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptor.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import javax.validation.metadata.ElementDescriptor;
+
+/**
+ * Description: This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface ParameterDescriptor extends ElementDescriptor {
+ /**
+ * Is the parameter marked by the <code>@Valid</code> annotation.
+ *
+ * @return <code>true</code> if the annotation is present, <code>false</code> otherwise.
+ */
+ boolean isCascaded();
+
+ /**
+ * Get the index of the referenced parameter.
+ * @return int
+ */
+ int getIndex();
+}
+
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptorImpl.java
new file mode 100644
index 0000000..2ca9bf0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptorImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import org.apache.bval.jsr303.ElementDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+
+/**
+ * Description: {@link ParameterDescriptor} implementation.<br/>
+ */
+public class ParameterDescriptorImpl extends ElementDescriptorImpl
+ implements ParameterDescriptor {
+ private boolean cascaded;
+ private int index;
+
+ /**
+ * Create a new ParameterDescriptorImpl instance.
+ * @param metaBean
+ * @param validations
+ */
+ public ParameterDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+ super(metaBean, metaBean.getClass(), validations);
+ }
+
+ /**
+ * Create a new ParameterDescriptorImpl instance.
+ * @param elementClass
+ * @param validations
+ */
+ public ParameterDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+ super(elementClass, validations);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascaded() {
+ return cascaded;
+ }
+
+ /**
+ * Set whether the referenced parameter descriptor should be validated.
+ * @param cascaded
+ */
+ public void setCascaded(boolean cascaded) {
+ this.cascaded = cascaded;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Set the index of the referenced parameter.
+ * @param index
+ */
+ public void setIndex(int index) {
+ this.index = index;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ProcedureDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ProcedureDescriptor.java
new file mode 100644
index 0000000..a8ef674
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ProcedureDescriptor.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+
+import org.apache.bval.model.MetaBean;
+
+import java.util.List;
+
+/**
+ * Description: superinterface of {@link ConstructorDescriptor} and {@link MethodDescriptor}.<br/>
+ */
+public interface ProcedureDescriptor {
+ /**
+ * Get the owning metabean.
+ * @return MetaBean
+ */
+ MetaBean getMetaBean();
+
+ /**
+ * Set whether this procedure should be validated.
+ * @param b
+ */
+ void setCascaded(boolean b);
+
+ /**
+ * Get the parameter descriptors of this procedure.
+ * @return {@link List} of {@link ParameterDescriptor}
+ */
+ List<ParameterDescriptor> getParameterDescriptors();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ReturnAccess.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ReturnAccess.java
new file mode 100644
index 0000000..261b52c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ReturnAccess.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303.extensions;
+
+import org.apache.bval.util.AccessStrategy;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+
+/**
+ * Implementation of {@link AccessStrategy} for method return values.
+ *
+ * @author Carlos Vara
+ */
+public class ReturnAccess extends AccessStrategy {
+
+ private Type returnType;
+
+ /**
+ * Create a new ReturnAccess instance.
+ * @param returnType
+ */
+ public ReturnAccess(Type returnType) {
+ this.returnType = returnType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(Object instance) {
+ throw new UnsupportedOperationException("Obtaining a method return value not yet implemented");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ElementType getElementType() {
+ return ElementType.METHOD;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Type getJavaType() {
+ return this.returnType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPropertyName() {
+ return "Return value";
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Group.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Group.java
new file mode 100644
index 0000000..3ba48eb
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Group.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.groups.Default;
+
+import org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * Immutable object that wraps an interface representing a single group.
+ */
+public final class Group {
+ /**
+ * the Default Group
+ */
+ public static final Group DEFAULT = new Group(Default.class);
+
+ private final Class<?> group;
+
+ /**
+ * Create a new Group instance.
+ * @param group
+ */
+ public Group(Class<?> group) {
+ this.group = group;
+ }
+
+ /**
+ * Get the actual group class.
+ * @return
+ */
+ public Class<?> getGroup() {
+ return group;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return "Group{" + "group=" + group + '}';
+ }
+
+ /**
+ * Learn whether the group represented is the default group.
+ * @return boolean
+ */
+ public boolean isDefault() {
+ return Default.class.equals(group);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o instanceof Group == false) return false;
+ return ObjectUtils.equals(group, ((Group) o).group);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return (group != null ? group.hashCode() : 0);
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Groups.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Groups.java
new file mode 100644
index 0000000..12e2526
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Groups.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+
+import javax.validation.GroupDefinitionException;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Defines the order to validate groups during validation.
+ * with some inspiration from reference implementation
+ *
+ * @author Roman Stumm
+ */
+public class Groups {
+ /** The list of single groups. */
+ protected List<Group> groups = new LinkedList<Group>();
+
+ /** The list of sequences. */
+ protected List<List<Group>> sequences = new LinkedList<List<Group>>();
+
+ /**
+ * Get the Groups.
+ * @return {@link List} of {@link Group}.
+ */
+ public List<Group> getGroups() {
+ return groups;
+ }
+
+ /**
+ * Get the Group sequences.
+ * @return {@link List} of {@link List} of {@link Group}
+ */
+ public List<List<Group>> getSequences() {
+ return sequences;
+ }
+
+ /**
+ * Insert a {@link Group}.
+ * @param group to insert
+ */
+ void insertGroup(Group group) {
+ if (!groups.contains(group)) {
+ groups.add(group);
+ }
+ }
+
+ /**
+ * Insert a sequence.
+ * @param groups {@link List} of {@link Group} to insert
+ */
+ void insertSequence(List<Group> groups) {
+ if (groups == null || groups.isEmpty()) {
+ return;
+ }
+
+ if (!sequences.contains(groups)) {
+ sequences.add(groups);
+ }
+ }
+
+ /**
+ * Assert that the default group can be expanded to <code>defaultGroups</code>.
+ * @param defaultGroups
+ */
+ public void assertDefaultGroupSequenceIsExpandable(List<Group> defaultGroups) {
+ for (List<Group> groupList : sequences) {
+ int idx = groupList.indexOf(Group.DEFAULT);
+ if (idx != -1) {
+ ensureExpandable(groupList, defaultGroups, idx);
+ }
+ }
+ }
+
+ private void ensureExpandable(List<Group> groupList, List<Group> defaultGroupList,
+ int defaultGroupIndex) {
+ for (int i = 0; i < defaultGroupList.size(); i++) {
+ Group group = defaultGroupList.get(i);
+ if (group.isDefault()) {
+ continue; // the default group is the one we want to replace
+ }
+ int index = groupList
+ .indexOf(group); // sequence contains group of default group sequence
+ if (index == -1) {
+ continue; // if group is not in the sequence
+ }
+
+ if ((i == 0 && index == defaultGroupIndex - 1) ||
+ (i == defaultGroupList.size() - 1 && index == defaultGroupIndex + 1)) {
+ // if we are at the beginning or end of he defaultGroupSequence and the
+ // matches are either directly before or after we can continue,
+ // since we basically have two groups
+ continue;
+ }
+ throw new GroupDefinitionException("Unable to expand default group list" +
+ defaultGroupList + " into sequence " + groupList);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/GroupsComputer.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/GroupsComputer.java
new file mode 100644
index 0000000..49b8803
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/GroupsComputer.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.GroupSequence;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import java.util.*;
+
+/**
+ * Description: compute group order, based on the RI behavior as to guarantee
+ * compatibility with interpretations of the spec.<br/>
+ * Implementation is thread-safe.
+ */
+public class GroupsComputer {
+ /** The default group array used in case any of the validate methods is called without a group. */
+ private static final Groups DEFAULT_GROUPS;
+
+ static {
+ DEFAULT_GROUPS = new GroupsComputer().computeGroups(Arrays.asList(getDefaultGroupArray()));
+ }
+
+ /**
+ * Get the default group array.
+ * @return <code>{@link Default}.class</code> only
+ */
+ public static Class<?>[] getDefaultGroupArray() {
+ return new Class<?>[] { Default.class };
+ }
+
+ /** caching resolved groups in a thread-safe map. */
+ private final Map<Class<?>, List<Group>> resolvedSequences = Collections
+ .synchronizedMap(new HashMap<Class<?>, List<Group>>());
+
+ /**
+ * Compute groups from an array of group classes.
+ * @param groups
+ * @return {@link Groups}
+ */
+ public Groups computeGroups(Class<?>[] groups) {
+ if (groups == null) {
+ throw new IllegalArgumentException("null passed as group");
+ }
+
+ // if no groups is specified use the default
+ if (groups.length == 0) {
+ return DEFAULT_GROUPS;
+ }
+
+ return computeGroups(Arrays.asList(groups));
+ }
+
+ /**
+ * Main compute implementation.
+ * @param groups
+ * @return {@link Groups}
+ */
+ protected Groups computeGroups(Collection<Class<?>> groups) {
+ if (groups == null || groups.size() == 0) {
+ throw new IllegalArgumentException("At least one group has to be specified.");
+ }
+
+ for (Class<?> clazz : groups) {
+ if (!clazz.isInterface()) {
+ throw new ValidationException(
+ "A group has to be an interface. " + clazz.getName() + " is not.");
+ }
+ }
+
+ Groups chain = new Groups();
+ for (Class<?> clazz : groups) {
+ GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
+ if (anno == null) {
+ Group group = new Group(clazz);
+ chain.insertGroup(group);
+ insertInheritedGroups(clazz, chain);
+ } else {
+ insertSequence(clazz, anno, chain);
+ }
+ }
+
+ return chain;
+ }
+
+ private void insertInheritedGroups(Class<?> clazz, Groups chain) {
+ for (Class<?> extendedInterface : clazz.getInterfaces()) {
+ Group group = new Group(extendedInterface);
+ chain.insertGroup(group);
+ insertInheritedGroups(extendedInterface, chain);
+ }
+ }
+
+ private void insertSequence(Class<?> clazz, GroupSequence anno, Groups chain) {
+ List<Group> sequence;
+ if (resolvedSequences.containsKey(clazz)) {
+ sequence = resolvedSequences.get(clazz);
+ } else {
+ sequence = resolveSequence(clazz, anno, new HashSet<Class<?>>());
+ }
+ chain.insertSequence(sequence);
+ }
+
+ private List<Group> resolveSequence(Class<?> group, GroupSequence sequenceAnnotation,
+ Set<Class<?>> processedSequences) {
+ if (processedSequences.contains(group)) {
+ throw new GroupDefinitionException("Cyclic dependency in groups definition");
+ } else {
+ processedSequences.add(group);
+ }
+ List<Group> resolvedGroupSequence = new LinkedList<Group>();
+ Class<?>[] sequenceArray = sequenceAnnotation.value();
+ for (Class<?> clazz : sequenceArray) {
+ GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
+ if (anno == null) {
+ resolvedGroupSequence.add(new Group(clazz)); // group part of sequence
+ } else {
+ List<Group> tmpSequence =
+ resolveSequence(clazz, anno, processedSequences); // recursion!
+ resolvedGroupSequence.addAll(tmpSequence);
+ }
+ }
+ resolvedSequences.put(group, resolvedGroupSequence);
+ return resolvedGroupSequence;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingRelevant.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingRelevant.java
new file mode 100644
index 0000000..5b9502e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingRelevant.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.resolver;
+
+import javax.validation.TraversableResolver;
+
+/**
+ * Description: indicator interface to let the implementation choose
+ * whether results of traversable resolver should be cached.<br/>
+ */
+public interface CachingRelevant {
+ /**
+ * Learn whether the results of the {@link TraversableResolver} should be cached.
+ * @return boolean
+ */
+ boolean needsCaching();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingTraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingTraversableResolver.java
new file mode 100644
index 0000000..bf56680
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingTraversableResolver.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303.resolver;
+
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import java.lang.annotation.ElementType;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Cache results of a delegated traversable resovler to optimize calls
+ * It works only for a single validate* call and should not be used if
+ * the TraversableResolver is accessed concurrently
+ * <p/>
+ * Date: 25.11.2009 <br/>
+ * Time: 13:56:18 <br/>
+ *
+ * @author Roman Stumm (based on the code of Emmanuel Bernard)
+ */
+public class CachingTraversableResolver implements TraversableResolver, CachingRelevant {
+ private TraversableResolver delegate;
+ private Map<CacheEntry, CacheEntry> cache = new HashMap<CacheEntry, CacheEntry>();
+
+ /**
+ * Convenience method to check whether caching is necessary on a given {@link TraversableResolver}.
+ * @param resolver to check
+ * @return true when a CachingTraversableResolver is to be used during validation
+ */
+ public static boolean needsCaching(TraversableResolver resolver) {
+ // caching, if we do not know exactly
+ return !(resolver instanceof CachingRelevant) ||
+ ((CachingRelevant) resolver).needsCaching();
+ }
+
+ /**
+ * Create a new CachingTraversableResolver instance.
+ * @param delegate
+ */
+ public CachingTraversableResolver(TraversableResolver delegate) {
+ this.delegate = delegate;
+ }
+
+ /**
+ * If necessary, return a caching wrapper for the specified {@link TraversableResolver}.
+ * @param traversableResolver
+ * @return {@link TraversableResolver}
+ * @see #needsCaching(TraversableResolver)
+ */
+ public static TraversableResolver cacheFor(TraversableResolver traversableResolver) {
+ if (needsCaching(traversableResolver)) {
+ return new CachingTraversableResolver(traversableResolver);
+ } else {
+ return traversableResolver;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ CacheEntry currentLH = new CacheEntry(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ CacheEntry cachedLH = cache.get(currentLH);
+ if (cachedLH == null) {
+ currentLH.reachable = delegate.isReachable(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ cache.put(currentLH, currentLH);
+ cachedLH = currentLH;
+ } else if (cachedLH.reachable == null) {
+ cachedLH.reachable = delegate.isReachable(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ }
+ return cachedLH.reachable;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ CacheEntry currentLH = new CacheEntry(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ CacheEntry cachedLH = cache.get(currentLH);
+ if (cachedLH == null) {
+ currentLH.cascadable = delegate.isCascadable(traversableObject,
+ traversableProperty, rootBeanType, pathToTraversableObject, elementType);
+ cache.put(currentLH, currentLH);
+ cachedLH = currentLH;
+ } else if (cachedLH.cascadable == null) {
+ cachedLH.cascadable = delegate.isCascadable(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ }
+ return cachedLH.cascadable;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean needsCaching() {
+ return false; // I am the cache. Do not need cache for cache
+ }
+
+ /**
+ * Entry in the cache.
+ */
+ private static class CacheEntry {
+ private final Object object;
+ private final Path.Node node;
+ private final Class<?> type;
+ private final Path path;
+ private final ElementType elementType;
+ private final int hashCode;
+
+ private Boolean reachable;
+ private Boolean cascadable;
+
+ /**
+ * Create a new CacheEntry instance.
+ * @param traversableObject
+ * @param traversableProperty
+ * @param rootBeanType
+ * @param pathToTraversableObject
+ * @param elementType
+ */
+ private CacheEntry(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ this.object = traversableObject;
+ this.node = traversableProperty;
+ this.type = rootBeanType;
+ this.path = pathToTraversableObject;
+ this.elementType = elementType;
+ this.hashCode = buildHashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CacheEntry that = (CacheEntry) o;
+
+ return elementType == that.elementType && path.equals(that.path) &&
+ type.equals(that.type) &&
+ !(object != null ? !object.equals(that.object) : that.object != null) &&
+ node.equals(that.node);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ private int buildHashCode() {
+ int result = object != null ? object.hashCode() : 0;
+ result = 31 * result + node.hashCode();
+ result = 31 * result + type.hashCode();
+ result = 31 * result + path.hashCode();
+ result = 31 * result + elementType.hashCode();
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/DefaultTraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/DefaultTraversableResolver.java
new file mode 100644
index 0000000..e6fc000
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/DefaultTraversableResolver.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303.resolver;
+
+import java.lang.annotation.ElementType;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.util.PrivilegedActions;
+import org.apache.commons.lang3.ClassUtils;
+
+/** @see javax.validation.TraversableResolver */
+public class DefaultTraversableResolver implements TraversableResolver, CachingRelevant {
+ private static final Logger log = Logger.getLogger(DefaultTraversableResolver.class.getName());
+
+ /** Class to load to check whether JPA 2 is on the classpath. */
+ private static final String PERSISTENCE_UTIL_CLASSNAME =
+ "javax.persistence.PersistenceUtil";
+
+ /** Class to instantiate in case JPA 2 is on the classpath. */
+ private static final String JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME =
+ "org.apache.bval.jsr303.resolver.JPATraversableResolver";
+
+
+ private TraversableResolver jpaTR;
+
+ /**
+ * Create a new DefaultTraversableResolver instance.
+ */
+ public DefaultTraversableResolver() {
+ initJpa();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ return jpaTR == null || jpaTR.isReachable(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ return jpaTR == null || jpaTR.isCascadable(traversableObject, traversableProperty,
+ rootBeanType, pathToTraversableObject, elementType);
+ }
+
+ /** Tries to load detect and load JPA. */
+ @SuppressWarnings("unchecked")
+ private void initJpa() {
+ final ClassLoader classLoader = getClassLoader();
+ try {
+ PrivilegedActions.getClass(classLoader, PERSISTENCE_UTIL_CLASSNAME);
+ log.log(Level.FINEST, String.format("Found %s on classpath.", PERSISTENCE_UTIL_CLASSNAME));
+ } catch (Exception e) {
+ log.log(Level.FINEST, String.format("Cannot find %s on classpath. All properties will per default be traversable.", PERSISTENCE_UTIL_CLASSNAME));
+ return;
+ }
+
+ try {
+ Class<? extends TraversableResolver> jpaAwareResolverClass =
+ (Class<? extends TraversableResolver>)
+ ClassUtils.getClass(classLoader, JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME, true);
+ jpaTR = jpaAwareResolverClass.newInstance();
+ log.log(Level.FINEST, String.format("Instantiated an instance of %s.", JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME));
+ } catch (Exception e) {
+ log.log(Level.WARNING,
+ String.format(
+ "Unable to load or instantiate JPA aware resolver %s. All properties will per default be traversable.",
+ JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME, e));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean needsCaching() {
+ return jpaTR != null && CachingTraversableResolver.needsCaching(jpaTR);
+ }
+
+ private static ClassLoader getClassLoader()
+ {
+ return (System.getSecurityManager() == null)
+ ? getClassLoader0()
+ : AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ return getClassLoader0();
+ }
+ });
+ }
+
+ private static ClassLoader getClassLoader0()
+ {
+ final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ return (loader != null) ? loader : ClassHelper.class.getClassLoader();
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/JPATraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/JPATraversableResolver.java
new file mode 100644
index 0000000..409ac52
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/JPATraversableResolver.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303.resolver;
+
+import javax.persistence.Persistence;
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import java.lang.annotation.ElementType;
+
+
+/** @see javax.validation.TraversableResolver */
+public class JPATraversableResolver implements TraversableResolver, CachingRelevant {
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ return traversableObject == null || Persistence.getPersistenceUtil()
+ .isLoaded(traversableObject, traversableProperty.getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean needsCaching() {
+ return true; // yes
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/SimpleTraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/SimpleTraversableResolver.java
new file mode 100644
index 0000000..ff5a3a3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/SimpleTraversableResolver.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.resolver;
+
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import java.lang.annotation.ElementType;
+
+/**
+ * Description: traversable resolver that does always resolve.<br/>
+ */
+public class SimpleTraversableResolver implements TraversableResolver, CachingRelevant {
+ /**
+ * {@inheritDoc}
+ * @return <code>true</code>
+ */
+ public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ java.lang.annotation.ElementType elementType) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return <code>true</code>
+ */
+ public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+ Class<?> rootBeanType, Path pathToTraversableObject,
+ ElementType elementType) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @return <code>false</code>
+ */
+ public boolean needsCaching() {
+ return false; // no
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java
new file mode 100644
index 0000000..eeaf301
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import org.apache.commons.lang3.ClassUtils;
+
+import java.security.AccessController;
+import java.util.List;
+
+/**
+ * Common operations on classes that do not require an {@link AccessController}.
+ *
+ * @author Carlos Vara
+ */
+public class ClassHelper {
+
+ private ClassHelper() {
+ // No instances please
+ }
+
+ /**
+ * Fill the list with the full class/interface hierarchy of the given class.
+ * List is ordered from the most to less specific.
+ *
+ * @param allClasses
+ * The current list of classes in the hierarchy.
+ * @param clazz
+ * The current class, root of the hierarchy to traverse.
+ */
+ static public void fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) {
+ if (clazz == null || clazz == Object.class) {
+ return;
+ }
+ if (allClasses.contains(clazz)) {
+ return;
+ }
+ allClasses.add(clazz);
+ fillFullClassHierarchyAsList(allClasses, clazz.getSuperclass());
+ for (Class<?> subClass : clazz.getInterfaces()) {
+ fillFullClassHierarchyAsList(allClasses, subClass);
+ }
+ }
+
+ /**
+ * @deprecated Will be removed for security reasons.
+ *
+ * Perform ClassUtils.getClass functions with Java 2 Security enabled.
+ */
+ @Deprecated
+ public static Class<?> getClass(String className) throws ClassNotFoundException {
+ return getClass(className, true);
+ }
+
+ /**
+ * @deprecated Will be removed for security reasons.
+ *
+ * Perform ClassUtils.getClass functions with Java 2 Security enabled.
+ */
+ @Deprecated
+ public static Class<?> getClass(String className, boolean initialize) throws ClassNotFoundException {
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if (loader == null)
+ loader = ClassHelper.class.getClassLoader();
+ return ClassUtils.getClass(loader, className, initialize);
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java
new file mode 100644
index 0000000..4b7b48b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+
+/**
+ * Internal validator that ensures the correct definition of constraint
+ * annotations.
+ *
+ * @author Carlos Vara
+ */
+public class ConstraintDefinitionValidator {
+
+ /**
+ * Ensures that the constraint definition is valid.
+ *
+ * @param annotation
+ * An annotation which is annotated with {@link Constraint}.
+ * @throws ConstraintDefinitionException
+ * In case the constraint is invalid.
+ */
+ public static void validateConstraintDefinition(Annotation annotation) {
+ ConstraintAnnotationAttributes.GROUPS.validateOn(annotation.annotationType());
+ ConstraintAnnotationAttributes.PAYLOAD.validateOn(annotation.annotationType());
+ ConstraintAnnotationAttributes.MESSAGE.validateOn(annotation.annotationType());
+ validAttributes(annotation);
+ }
+
+ /**
+ * Check that the annotation has no methods that start with "valid".
+ *
+ * @param annotation
+ * The annotation to check.
+ */
+ private static void validAttributes(final Annotation annotation) {
+ final Method[] methods = run(SecureActions.getDeclaredMethods(annotation.annotationType()));
+ for (Method method : methods ){
+ // Currently case insensitive, the spec is unclear about this
+ if (method.getName().toLowerCase(Locale.ENGLISH).startsWith("valid")) {
+ throw new ConstraintDefinitionException(
+ "A constraint annotation cannot have methods which start with 'valid'");
+ }
+ }
+ }
+
+ private static <T> T run(PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/EnumerationConverter.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/EnumerationConverter.java
new file mode 100644
index 0000000..3e1394f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/EnumerationConverter.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import org.apache.commons.beanutils.Converter;
+
+/**
+ * A {@code org.apache.commons.beanutils.Converter} implementation to handle
+ * Enumeration type.
+ *
+ * $Id$
+ */
+public final class EnumerationConverter implements Converter {
+
+ /**
+ * The static converter instance.
+ */
+ private static final EnumerationConverter INSTANCE = new EnumerationConverter();
+
+ /**
+ * Returns this converter instance.
+ *
+ * @return this converter instance.
+ */
+ public static EnumerationConverter getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * This class can't be instantiated.
+ */
+ private EnumerationConverter() {
+ // do nothing
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public Object convert(Class type, Object value) {
+ if (!type.isEnum()) {
+ throw new RuntimeException("Only enum types supported in this version!");
+ }
+
+ if (value == null) {
+ throw new RuntimeException("Null values not supported in this version!");
+ }
+
+ if (String.class != value.getClass()) {
+ throw new RuntimeException("Only java.lang.String values supported in this version!");
+ }
+
+ String stringValue = (String) value;
+
+ final Class<Enum> enumClass = (Class<Enum>) type;
+ return Enum.valueOf(enumClass, stringValue);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/IOUtils.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/IOUtils.java
new file mode 100644
index 0000000..ae0653b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/IOUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Commons IO utilities.
+ *
+ * $Id$
+ */
+public final class IOUtils {
+
+ /**
+ * This class can't be instantiated
+ */
+ private IOUtils() {
+ // do nothing
+ }
+
+ /**
+ * Close quietly a {@link Closeable} reference.
+ *
+ * @param closeable the {@link Closeable} has to be closed.
+ */
+ public static void closeQuietly(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderCustomizableContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderCustomizableContextImpl.java
new file mode 100644
index 0000000..114104b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderCustomizableContextImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+
+import org.apache.bval.jsr303.ConstraintValidatorContextImpl;
+
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: implementation of {@link NodeBuilderCustomizableContext}.<br/>
+ */
+final class NodeBuilderCustomizableContextImpl
+ implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext {
+ private final ConstraintValidatorContextImpl parent;
+ private final String messageTemplate;
+ private final PathImpl propertyPath;
+ private NodeImpl node;
+
+ /**
+ * Create a new NodeBuilderCustomizableContextImpl instance.
+ * @param contextImpl
+ * @param template
+ * @param path
+ * @param name
+ */
+ NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl contextImpl, String template,
+ PathImpl path, String name) {
+ parent = contextImpl;
+ messageTemplate = template;
+ propertyPath = path;
+ node = new NodeImpl(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() {
+ // Modifies the "previous" node in the path
+ node.setInIterable(true);
+ return new NodeContextBuilderImpl(parent, messageTemplate, propertyPath, node);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(
+ String name) {
+ propertyPath.addNode(node);
+ node = new NodeImpl(name);
+ return this; // Re-use this instance
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext addConstraintViolation() {
+ propertyPath.addNode(node);
+ node = null;
+ parent.addError(messageTemplate, propertyPath);
+ return parent;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderDefinedContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderDefinedContextImpl.java
new file mode 100644
index 0000000..eb666c2
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderDefinedContextImpl.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+
+import org.apache.bval.jsr303.ConstraintValidatorContextImpl;
+
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: Implementation of {@link NodeBuilderDefinedContext}.<br/>
+ */
+public final class NodeBuilderDefinedContextImpl
+ implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext {
+ private final ConstraintValidatorContextImpl parent;
+ private final String messageTemplate;
+ private final PathImpl propertyPath;
+
+ /**
+ * Create a new NodeBuilderDefinedContextImpl instance.
+ * @param contextImpl
+ * @param template
+ * @param path
+ */
+ public NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl contextImpl, String template,
+ PathImpl path) {
+ parent = contextImpl;
+ messageTemplate = template;
+ propertyPath = path;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(
+ String name) {
+ // Node not yet added, wait until more information is provided
+ return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext addConstraintViolation() {
+ parent.addError(messageTemplate, propertyPath);
+ return parent;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeContextBuilderImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeContextBuilderImpl.java
new file mode 100644
index 0000000..d6ab6ae
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeContextBuilderImpl.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+
+import org.apache.bval.jsr303.ConstraintValidatorContextImpl;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder;
+
+/**
+ * Description: Implementation of {@link NodeContextBuilder}.<br/>
+ */
+final class NodeContextBuilderImpl
+ implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder {
+ private final ConstraintValidatorContextImpl parent;
+ private final String messageTemplate;
+ private final PathImpl propertyPath;
+ // The name of the last "added" node, it will only be added if it has a non-null name
+ // The actual incorporation in the path will take place when the definition of the current leaf node is complete
+ private final NodeImpl node;
+
+ /**
+ * Create a new NodeContextBuilderImpl instance.
+ * @param contextImpl
+ * @param template
+ * @param path
+ */
+ NodeContextBuilderImpl(ConstraintValidatorContextImpl contextImpl,
+ String template, PathImpl path, NodeImpl node) {
+ parent = contextImpl;
+ messageTemplate = template;
+ propertyPath = path;
+ this.node = node;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(
+ Object key) {
+ node.setKey(key);
+ propertyPath.addNode(node);
+ return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(
+ Integer index) {
+ node.setIndex(index);
+ propertyPath.addNode(node);
+ return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(
+ String name) {
+ propertyPath.addNode(node);
+ return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ConstraintValidatorContext addConstraintViolation() {
+ propertyPath.addNode(node);
+ parent.addError(messageTemplate, propertyPath);
+ return parent;
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeImpl.java
new file mode 100644
index 0000000..1d851ae
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeImpl.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import javax.validation.Path;
+import javax.validation.Path.Node;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Description: a node (property) as part of a Path.
+ * (Implementation based on reference implementation) <br/>
+ */
+public final class NodeImpl implements Path.Node, Serializable {
+
+ private static final long serialVersionUID = 1L;
+ private static final String INDEX_OPEN = "[";
+ private static final String INDEX_CLOSE = "]";
+
+ /**
+ * Append a Node to the specified StringBuilder.
+ * @param node
+ * @param to
+ * @return to
+ */
+ public static StringBuilder appendNode(Node node, StringBuilder to) {
+ if (node.isInIterable()) {
+ to.append(INDEX_OPEN);
+ if (node.getIndex() != null) {
+ to.append(node.getIndex());
+ } else if (node.getKey() != null) {
+ to.append(node.getKey());
+ }
+ to.append(INDEX_CLOSE);
+ }
+ if (node.getName() != null) {
+ if (to.length() > 0) {
+ to.append(PathImpl.PROPERTY_PATH_SEPARATOR);
+ }
+ to.append(node.getName());
+ }
+ return to;
+ }
+
+ /**
+ * Get a NodeImpl indexed from the preceding node (or root).
+ * @param index
+ * @return NodeImpl
+ */
+ public static NodeImpl atIndex(Integer index) {
+ NodeImpl result = new NodeImpl();
+ result.setIndex(index);
+ return result;
+ }
+
+ /**
+ * Get a NodeImpl keyed from the preceding node (or root).
+ * @param key
+ * @return NodeImpl
+ */
+ public static NodeImpl atKey(Object key) {
+ NodeImpl result = new NodeImpl();
+ result.setKey(key);
+ return result;
+ }
+
+ private String name;
+ private boolean inIterable;
+ private Integer index;
+ private Object key;
+
+ /**
+ * Create a new NodeImpl instance.
+ * @param name
+ */
+ public NodeImpl(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Create a new NodeImpl instance.
+ * @param node
+ */
+ NodeImpl(Path.Node node) {
+ this.name = node.getName();
+ this.inIterable = node.isInIterable();
+ this.index = node.getIndex();
+ this.key = node.getKey();
+ }
+
+ private NodeImpl() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name the name to set
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isInIterable() {
+ return inIterable;
+ }
+
+ /**
+ * Set whether this node represents a contained value of an {@link Iterable} or {@link Map}.
+ * @param inIterable
+ */
+ public void setInIterable(boolean inIterable) {
+ this.inIterable = inIterable;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Integer getIndex() {
+ return index;
+ }
+
+ /**
+ * Set the index of this node, implying <code>inIterable</code>.
+ * @param index
+ */
+ public void setIndex(Integer index) {
+ inIterable = true;
+ this.index = index;
+ this.key = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object getKey() {
+ return key;
+ }
+
+ /**
+ * Set the map key of this node, implying <code>inIterable</code>.
+ * @param key
+ */
+ public void setKey(Object key) {
+ inIterable = true;
+ this.key = key;
+ this.index = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return appendNode(this, new StringBuilder()).toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ NodeImpl node = (NodeImpl) o;
+
+ if (inIterable != node.inIterable) {
+ return false;
+ }
+ if (index != null ? !index.equals(node.index) : node.index != null) {
+ return false;
+ }
+ if (key != null ? !key.equals(node.key) : node.key != null) {
+ return false;
+ }
+ if (name != null ? !name.equals(node.name) : node.name != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (inIterable ? 1 : 0);
+ result = 31 * result + (index != null ? index.hashCode() : 0);
+ result = 31 * result + (key != null ? key.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathImpl.java
new file mode 100644
index 0000000..ba829e0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathImpl.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import javax.validation.Path;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Description: object holding the property path as a list of nodes.
+ * (Implementation partially based on reference implementation)
+ * <br/>
+ * This class is not synchronized.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PathImpl implements Path, Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ static final String PROPERTY_PATH_SEPARATOR = ".";
+
+ /**
+ * Builds non-root paths from expressions.
+ */
+ private static class PathImplBuilder implements PathNavigation.Callback<PathImpl> {
+ PathImpl result = new PathImpl();
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleProperty(String name) {
+ result.addProperty(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleIndexOrKey(String value) {
+ // with no context to guide us, we can only parse ints and fall back to String keys:
+ NodeImpl node;
+ try {
+ node = NodeImpl.atIndex(Integer.parseInt(value));
+ } catch (NumberFormatException e) {
+ node = NodeImpl.atKey(value);
+ }
+ result.addNode(node);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PathImpl result() {
+ if (result.nodeList.isEmpty()) {
+ throw new IllegalStateException();
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleGenericInIterable() {
+ result.addNode(NodeImpl.atIndex(null));
+ }
+
+ }
+
+ private final List<Node> nodeList;
+
+ /**
+ * Returns a {@code Path} instance representing the path described by the given string. To create a root node the
+ * empty string should be passed. Note: This signature is to maintain pluggability with the RI impl.
+ *
+ * @param propertyPath
+ * the path as string representation.
+ * @return a {@code Path} instance representing the path described by the given string.
+ */
+ public static PathImpl createPathFromString(String propertyPath) {
+ if (propertyPath == null || propertyPath.length() == 0) {
+ return create(null);
+ }
+ return PathNavigation.navigateAndReturn(propertyPath, new PathImplBuilder());
+ }
+
+ /**
+ * Create a {@link PathImpl} instance representing the specified path.
+ *
+ * @param name
+ * @return PathImpl
+ */
+ public static PathImpl create(String name) {
+ PathImpl path = new PathImpl();
+ NodeImpl node = new NodeImpl(name);
+ path.addNode(node);
+ return path;
+ }
+
+ /**
+ * Copy another Path.
+ *
+ * @param path
+ * @return new {@link PathImpl}
+ */
+ public static PathImpl copy(Path path) {
+ return path == null ? null : new PathImpl(path);
+ }
+
+ private PathImpl(Path path) {
+ this.nodeList = new ArrayList<Node>();
+ for (Object aPath : path) {
+ nodeList.add(new NodeImpl((Node) aPath));
+ }
+ }
+
+ private PathImpl() {
+ nodeList = new ArrayList<Node>();
+ }
+
+ private PathImpl(List<Node> nodeList) {
+ this.nodeList = new ArrayList<Node>();
+ for (Node node : nodeList) {
+ this.nodeList.add(new NodeImpl(node));
+ }
+ }
+
+ /**
+ * Learn whether this {@link PathImpl} points to the root of its graph.
+ *
+ * @return true if no child nodes
+ */
+ // our implementation stores a nameless root node.
+ public boolean isRootPath() {
+ if (nodeList.size() != 1) {
+ return false;
+ }
+ Path.Node first = nodeList.get(0);
+ return !first.isInIterable() && first.getName() == null;
+ }
+
+ /**
+ * Return a new {@link PathImpl} that represents <code>this</code> minus its leaf node (if present).
+ *
+ * @return PathImpl
+ */
+ public PathImpl getPathWithoutLeafNode() {
+ List<Node> nodes = new ArrayList<Node>(nodeList);
+ PathImpl path = null;
+ if (nodes.size() > 1) {
+ nodes.remove(nodes.size() - 1);
+ path = new PathImpl(nodes);
+ }
+ return path;
+ }
+
+ /**
+ * Add a node to this {@link PathImpl}.
+ *
+ * @param node
+ * to add
+ */
+ public void addNode(Node node) {
+ if (isRootPath()) {
+ nodeList.set(0, node);
+ } else {
+ nodeList.add(node);
+ }
+ }
+
+ /**
+ * Encapsulate the node manipulations needed to add a named property to this path.
+ *
+ * @param name
+ */
+ public void addProperty(String name) {
+ if (!nodeList.isEmpty()) {
+ NodeImpl leaf = getLeafNode();
+ if (leaf != null && leaf.isInIterable() && leaf.getName() == null) {
+ leaf.setName(name);
+ return;
+ }
+ }
+ addNode(new NodeImpl(name));
+ }
+
+ /**
+ * Trim the leaf node from this {@link PathImpl}.
+ *
+ * @return the node removed
+ * @throws IllegalStateException
+ * if no nodes are found
+ */
+ public Node removeLeafNode() {
+ if (isRootPath() || nodeList.size() == 0) {
+ throw new IllegalStateException("No nodes in path!");
+ }
+ try {
+ return nodeList.remove(nodeList.size() - 1);
+ } finally {
+ if (nodeList.isEmpty()) {
+ nodeList.add(new NodeImpl((String) null));
+ }
+ }
+ }
+
+ /**
+ * Get the leaf node (if any) from this {@link PathImpl}
+ *
+ * @return {@link NodeImpl}
+ */
+ public NodeImpl getLeafNode() {
+ if (nodeList.size() == 0) {
+ return null;
+ }
+ return (NodeImpl) nodeList.get(nodeList.size() - 1);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<Path.Node> iterator() {
+ return nodeList.iterator();
+ }
+
+ /**
+ * Learn whether <code>path</code> is a parent to <code>this</code>.
+ *
+ * @param path
+ * @return <code>true</code> if our nodes begin with nodes equal to those found in <code>path</code>
+ */
+ public boolean isSubPathOf(Path path) {
+ if (path instanceof PathImpl && ((PathImpl) path).isRootPath()) {
+ return true;
+ }
+ Iterator<Node> pathIter = path.iterator();
+ Iterator<Node> thisIter = iterator();
+ while (pathIter.hasNext()) {
+ Node pathNode = pathIter.next();
+ if (!thisIter.hasNext()) {
+ return false;
+ }
+ Node thisNode = thisIter.next();
+ if (pathNode.isInIterable()) {
+ if (!thisNode.isInIterable()) {
+ return false;
+ }
+ if (pathNode.getIndex() != null && !pathNode.getIndex().equals(thisNode.getIndex())) {
+ return false;
+ }
+ if (pathNode.getKey() != null && !pathNode.getKey().equals(thisNode.getKey())) {
+ return false;
+ }
+ } else if (thisNode.isInIterable()) {
+ // in this case we have shown that the proposed parent is not
+ // indexed, and we are, thus the paths cannot match
+ return false;
+ }
+ if (pathNode.getName() == null || pathNode.getName().equals(thisNode.getName())) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ for (Path.Node node : this) {
+ NodeImpl.appendNode(node, builder);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ PathImpl path = (PathImpl) o;
+ return !(nodeList != null && !nodeList.equals(path.nodeList)) && !(nodeList == null && path.nodeList != null);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return nodeList != null ? nodeList.hashCode() : 0;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathNavigation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathNavigation.java
new file mode 100644
index 0000000..711a472
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathNavigation.java
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import java.io.StringWriter;
+import java.text.ParsePosition;
+
+import javax.validation.ValidationException;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/**
+ * Defines a path navigation algorithm and a means of interacting with same.
+ *
+ * @version $Rev: 1136233 $ $Date: 2011-06-15 17:49:27 -0500 (Wed, 15 Jun 2011) $
+ */
+public class PathNavigation {
+
+ /**
+ * Path traversal callback function interface.
+ */
+ public interface Callback<T> {
+ /**
+ * Handle a .-delimited property.
+ *
+ * @param name
+ */
+ void handleProperty(String name);
+
+ /**
+ * Handle an index or key embedded in [].
+ *
+ * @param value
+ */
+ void handleIndexOrKey(String value);
+
+ /**
+ * Handle contiguous [].
+ */
+ void handleGenericInIterable();
+
+ /**
+ * Return a result. Called after navigation is complete.
+ *
+ * @return result
+ */
+ T result();
+ }
+
+ /**
+ * Callback "procedure" that always returns null.
+ */
+ public static abstract class CallbackProcedure implements Callback<Object> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public final Object result() {
+ complete();
+ return null;
+ }
+
+ /**
+ * Complete this CallbackProcedure. Default implementation is noop.
+ */
+ protected void complete() {
+ }
+ }
+
+ /**
+ * Create a new PathNavigation instance.
+ */
+ private PathNavigation() {
+ }
+
+ /**
+ * Navigate a path using the specified callback, returning its result.
+ *
+ * @param <T>
+ * @param propertyPath
+ * , null is assumed empty/root
+ * @param callback
+ * @return T result
+ */
+ public static <T> T navigateAndReturn(CharSequence propertyPath, Callback<? extends T> callback) {
+ try {
+ parse(propertyPath == null ? "" : propertyPath, new PathPosition(callback));
+ } catch (ValidationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ValidationException(String.format("invalid property: %s", propertyPath), ex);
+ }
+ return callback.result();
+ }
+
+ /**
+ * Navigate a path using the specified callback.
+ *
+ * @param propertyPath
+ * @param callback
+ */
+ public static void navigate(CharSequence propertyPath, Callback<?> callback) {
+ navigateAndReturn(propertyPath, callback);
+ }
+
+ private static void parse(CharSequence path, PathPosition pos) throws Exception {
+ int len = path.length();
+ boolean sep = true;
+ while (pos.getIndex() < len) {
+ int here = pos.getIndex();
+ char c = path.charAt(here);
+ switch (c) {
+ case ']':
+ throw new IllegalStateException(String.format("Position %s: unexpected '%s'", here, c));
+ case '[':
+ handleIndex(path, pos.next());
+ break;
+ case '.':
+ if (sep) {
+ throw new IllegalStateException(String.format("Position %s: expected property, index/key, or end of expression", here));
+ }
+ sep = true;
+ pos.next();
+ // fall through:
+ default:
+ if (!sep) {
+ throw new IllegalStateException(String.format("Position %s: expected property path separator, index/key, or end of expression", here));
+ }
+ pos.handleProperty(parseProperty(path, pos));
+ }
+ sep = false;
+ }
+ }
+
+ private static String parseProperty(CharSequence path, PathPosition pos) throws Exception {
+ int len = path.length();
+ int start = pos.getIndex();
+ loop: while (pos.getIndex() < len) {
+ switch (path.charAt(pos.getIndex())) {
+ case '[':
+ case ']':
+ case '.':
+ break loop;
+ }
+ pos.next();
+ }
+ if (pos.getIndex() > start) {
+ return path.subSequence(start, pos.getIndex()).toString();
+ }
+ throw new IllegalStateException(String.format("Position %s: expected property", start));
+ }
+
+ /**
+ * Handles an index/key. If the text contained between [] is surrounded by a pair of " or ', it will be treated as a
+ * string which may contain Java escape sequences.
+ *
+ * @param path
+ * @param pos
+ * @throws Exception
+ */
+ private static void handleIndex(CharSequence path, PathPosition pos) throws Exception {
+ int len = path.length();
+ int start = pos.getIndex();
+ if (start < len) {
+ char first = path.charAt(pos.getIndex());
+ if (first == '"' || first == '\'') {
+ String s = parseQuotedString(path, pos);
+ if (s != null && path.charAt(pos.getIndex()) == ']') {
+ pos.handleIndexOrKey(s);
+ pos.next();
+ return;
+ }
+ }
+ // no quoted string; match ] greedily
+ while (pos.getIndex() < len) {
+ int here = pos.getIndex();
+ try {
+ if (path.charAt(here) == ']') {
+ if (here == start) {
+ pos.handleGenericInIterable();
+ } else {
+ pos.handleIndexOrKey(path.subSequence(start, here).toString());
+ }
+ return;
+ }
+ } finally {
+ pos.next();
+ }
+ }
+ }
+ throw new IllegalStateException(String.format("Position %s: unparsable index", start));
+ }
+
+ private static String parseQuotedString(CharSequence path, PathPosition pos) throws Exception {
+ int len = path.length();
+ int start = pos.getIndex();
+ if (start < len) {
+ char quote = path.charAt(start);
+ pos.next();
+ StringWriter w = new StringWriter();
+ while (pos.getIndex() < len) {
+ int here = pos.getIndex();
+ // look for matching quote
+ if (path.charAt(here) == quote) {
+ pos.next();
+ return w.toString();
+ }
+ int codePoints = StringEscapeUtils.UNESCAPE_JAVA.translate(path, here, w);
+ if (codePoints == 0) {
+ w.write(Character.toChars(Character.codePointAt(path, here)));
+ pos.next();
+ } else {
+ for (int i = 0; i < codePoints; i++) {
+ pos.plus(Character.charCount(Character.codePointAt(path, pos.getIndex())));
+ }
+ }
+ }
+ // if reached, reset due to no ending quote found
+ pos.setIndex(start);
+ }
+ return null;
+ }
+
+ /**
+ * ParsePosition/Callback
+ */
+ private static class PathPosition extends ParsePosition implements Callback<Object> {
+ final Callback<?> delegate;
+
+ /**
+ * Create a new {@link PathPosition} instance.
+ *
+ * @param delegate
+ */
+ private PathPosition(Callback<?> delegate) {
+ super(0);
+ this.delegate = delegate;
+ }
+
+ /**
+ * Increment and return this.
+ *
+ * @return this
+ */
+ public PathPosition next() {
+ return plus(1);
+ }
+
+ /**
+ * Increase position and return this.
+ *
+ * @param addend
+ * @return this
+ */
+ public PathPosition plus(int addend) {
+ setIndex(getIndex() + addend);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleProperty(String name) {
+ delegate.handleProperty(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleIndexOrKey(String value) {
+ delegate.handleIndexOrKey(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleGenericInIterable() {
+ delegate.handleGenericInIterable();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object result() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ /*
+ * Override equals to make findbugs happy;
+ * would simply ignore but doesn't seem to be possible at the inner class level
+ * without attaching the filter to the containing class.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ /*
+ * Override hashCode to make findbugs happy in the presence of overridden #equals :P
+ */
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/SecureActions.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/SecureActions.java
new file mode 100644
index 0000000..4cf5ce4
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/SecureActions.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.PrivilegedAction;
+
+import javax.validation.ValidatorFactory;
+
+import org.apache.bval.jsr303.ConfigurationImpl;
+import org.apache.bval.util.PrivilegedActions;
+
+/**
+ * Description: utility methods to perform actions with AccessController or without.<br/>
+ */
+public class SecureActions extends PrivilegedActions {
+
+ /**
+ * Create a privileged action to get the context classloader of the current thread.
+ *
+ * @see Thread#getContextClassLoader()
+ */
+ public static PrivilegedAction<ClassLoader> getContextClassLoader()
+ {
+ return SecureActions.GetContextClassLoader.instance;
+ }
+
+
+ /**
+ * Create a privileged action to get the named field declared by the specified class.
+ * The result of the action will be {@code null} if there is no such field.
+ */
+ public static PrivilegedAction<Field> getDeclaredField(final Class<?> clazz, final String fieldName) {
+ return new PrivilegedAction<Field>() {
+ public Field run() {
+ try {
+ final Field f = clazz.getDeclaredField(fieldName);
+ setAccessibility(f);
+ return f;
+ } catch (final NoSuchFieldException ex) {
+ return null;
+ }
+ }
+ };
+ }
+
+
+
+ /**
+ * Create a privileged action to get all fields declared by the specified class.
+ */
+ public static PrivilegedAction<Field[]> getDeclaredFields(final Class<?> clazz) {
+ return new PrivilegedAction<Field[]>() {
+ public Field[] run() {
+ final Field[] fields = clazz.getDeclaredFields();
+ if (fields.length > 0)
+ AccessibleObject.setAccessible(fields, true);
+ return fields;
+ }
+ };
+ }
+
+
+
+ /**
+ * Create a privileged action to get all methods declared by the specified class.
+ */
+ public static PrivilegedAction<Method[]> getDeclaredMethods(final Class<?> clazz) {
+ // XXX 2011-03-27 jw: Inconsistent behaviour.
+ // doGetDeclaredFields() is setting fields accessible, but here we don't.
+ return new PrivilegedAction<Method[]>() {
+ public Method[] run() {
+ return clazz.getDeclaredMethods();
+ }
+ };
+ }
+
+ /**
+ * Create a privileged action to get the named method declared by the specified class
+ * or by one of its ancestors.
+ * The result of the action will be {@code null} if there is no such method.
+ */
+ public static PrivilegedAction<Method> getPublicMethod(final Class<?> clazz, final String methodName) {
+ return new PrivilegedAction<Method>() {
+ public Method run() {
+ try {
+ return clazz.getMethod(methodName, (Class[]) null);
+ } catch (final NoSuchMethodException ex) {
+ return null;
+ }
+ }
+ };
+ }
+
+ private static void setAccessibility(Field field) {
+ // FIXME 2011-03-27 jw:
+ // - Why not simply call field.setAccessible(true)?
+ // - Fields can not be abstract.
+ if (!Modifier.isPublic(field.getModifiers()) || (
+ Modifier.isPublic(field.getModifiers()) &&
+ Modifier.isAbstract(field.getModifiers()))) {
+ field.setAccessible(true);
+ }
+ }
+
+ /**
+ * Create a privileged action for ConfigurationImpl.buildValidatorFactory.
+ */
+ public static PrivilegedAction<ValidatorFactory> doPrivBuildValidatorFactory(final ConfigurationImpl config) {
+ return new PrivilegedAction<ValidatorFactory>() {
+ public ValidatorFactory run() {
+ return config.doPrivBuildValidatorFactory();
+ }
+ };
+ }
+
+ private static final class GetContextClassLoader extends Object implements PrivilegedAction<ClassLoader> {
+
+ static final GetContextClassLoader instance = new GetContextClassLoader();
+
+ private GetContextClassLoader()
+ {
+ super();
+ }
+
+ public final ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java
new file mode 100644
index 0000000..766139d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import java.lang.reflect.Type;
+
+import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.jsr303.Jsr303MetaBeanFactory;
+import org.apache.bval.jsr303.UnknownPropertyException;
+import org.apache.bval.jsr303.util.PathNavigation.CallbackProcedure;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.IndexedAccess;
+import org.apache.bval.util.KeyedAccess;
+import org.apache.bval.util.PropertyAccess;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * {@link ValidationContext} traversal {@link CallbackProcedure}.
+ *
+ * @version $Rev: 1137074 $ $Date: 2011-06-17 18:20:30 -0500 (Fri, 17 Jun 2011) $
+ */
+public class ValidationContextTraversal extends CallbackProcedure {
+ private static class NullSafePropertyAccess extends PropertyAccess {
+
+ /**
+ * Create a new NullSafePropertyAccess instance.
+ *
+ * @param clazz
+ * @param propertyName
+ */
+ public NullSafePropertyAccess(Class<?> clazz, String propertyName) {
+ super(clazz, propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(Object bean) {
+ return bean == null ? null : super.get(bean);
+ }
+ }
+
+ private final ValidationContext<?> validationContext;
+ private Type type;
+ private Class<?> rawType;
+
+ /**
+ * Create a new {@link ValidationContextTraversal} instance.
+ *
+ * @param validationContext
+ */
+ public ValidationContextTraversal(ValidationContext<?> validationContext) {
+ this.validationContext = validationContext;
+ init();
+ }
+
+ /**
+ * Initialize from {@link ValidationContext}.
+ */
+ public void init() {
+ this.rawType = validationContext.getMetaBean().getBeanClass();
+ this.type = this.rawType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleIndexOrKey(String token) {
+ moveDownIfNecessary();
+
+ AccessStrategy access;
+ if (IndexedAccess.getJavaElementType(type) != null) {
+ try {
+ Integer index = token == null ? null : Integer.valueOf(token);
+ access = new IndexedAccess(type, index);
+ validationContext.setCurrentIndex(index);
+ } catch (NumberFormatException e) {
+ throw new UnknownPropertyException(String.format("Cannot parse %s as an array/iterable index", token),
+ e);
+ }
+ } else if (KeyedAccess.getJavaElementType(type) != null) {
+ access = new KeyedAccess(type, token);
+ validationContext.setCurrentKey(token);
+ } else {
+ throw new UnknownPropertyException(String.format("Cannot determine index/key type for %s", type));
+ }
+ Object value = validationContext.getBean();
+ Object child = value == null ? null : access.get(value);
+ setType(child == null ? access.getJavaType() : child.getClass());
+ validationContext.setBean(child,
+ validationContext.getMetaBean().resolveMetaBean(child == null ? rawType : child));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleProperty(String token) {
+ moveDownIfNecessary();
+
+ MetaBean metaBean = validationContext.getMetaBean();
+
+ if (metaBean instanceof DynamicMetaBean) {
+ metaBean = metaBean.resolveMetaBean(ObjectUtils.defaultIfNull(validationContext.getBean(), rawType));
+ validationContext.setMetaBean(metaBean);
+ }
+ MetaProperty mp = metaBean.getProperty(token);
+ if (mp == null) {
+ // TODO this could indicate a property hosted on a superclass; should we shunt the context traversal down a path based on that type?
+
+ PropertyAccess access = new PropertyAccess(rawType, token);
+ if (access.isKnown()) {
+ // add heretofore unknown, but valid, property on the fly:
+ mp = Jsr303MetaBeanFactory.addMetaProperty(metaBean, access);
+ } else {
+ throw new UnknownPropertyException("unknown property '" + token + "' in " + metaBean.getId());
+ }
+ }
+ validationContext.setMetaProperty(mp);
+ setType(mp.getType());
+ }
+
+ /**
+ * If we currently have a property, navigate the context such that the property becomes the bean, in preparation for
+ * another property.
+ *
+ * @param validationContext
+ */
+ public void moveDownIfNecessary() {
+ MetaProperty mp = validationContext.getMetaProperty();
+ if (mp != null) {
+ if (mp.getMetaBean() == null) {
+ throw new UnknownPropertyException(String.format("Property %s.%s is not cascaded", mp
+ .getParentMetaBean().getId(), mp.getName()));
+ }
+ validationContext.moveDown(mp, new NullSafePropertyAccess(validationContext.getMetaBean().getBeanClass(),
+ mp.getName()));
+ }
+ }
+
+ /**
+ * Set the type of the expression processed thus far.
+ *
+ * @param type
+ */
+ protected void setType(Type type) {
+ this.rawType = TypeUtils.getRawType(type, this.type);
+ this.type = type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void handleGenericInIterable() {
+ throw new UnsupportedOperationException("Cannot navigate a ValidationContext to []");
+ }
+
+ /**
+ * @return the type
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * @return the rawType
+ */
+ public Class<?> getRawType() {
+ return rawType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void complete() {
+ super.complete();
+ if (validationContext.getMetaProperty() != null) {
+ return;
+ }
+ if (validationContext.getMetaBean() instanceof DynamicMetaBean) {
+ validationContext.setMetaBean(validationContext.getMetaBean().resolveMetaBean(
+ ObjectUtils.defaultIfNull(validationContext.getBean(), rawType)));
+ }
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationIgnores.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationIgnores.java
new file mode 100644
index 0000000..fa508be
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationIgnores.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: This class instantiated during the parsing of the XML configuration
+ * data and keeps track of the annotations which should be ignored.<br/>
+ */
+public final class AnnotationIgnores {
+
+ private static final Logger log = Logger.getLogger(AnnotationIgnores.class.getName());
+
+ /**
+ * Keeps track whether the 'ignore-annotations' flag is set on bean level in the
+ * xml configuration.
+ * If 'ignore-annotations' is not specified: default = true
+ */
+ private final Map<Class<?>, Boolean> ignoreAnnotationDefaults =
+ new HashMap<Class<?>, Boolean>();
+
+ /**
+ * Keeps track of explicitly excluded members (fields and properties) for a given class.
+ * If a member appears in
+ * the list mapped to a given class 'ignore-annotations' was explicitly set to
+ * <code>true</code> in the configuration
+ * for this class.
+ */
+ private final Map<Class<?>, List<Member>> ignoreAnnotationOnMember =
+ new HashMap<Class<?>, List<Member>>();
+
+ private final Map<Class<?>, Boolean> ignoreAnnotationOnClass =
+ new HashMap<Class<?>, Boolean>();
+
+ /**
+ * Record the ignore state for a particular annotation type.
+ * @param clazz
+ * @param b, default true if null
+ */
+ public void setDefaultIgnoreAnnotation(Class<?> clazz, Boolean b) {
+ ignoreAnnotationDefaults.put(clazz, b == null || b.booleanValue());
+ }
+
+ /**
+ * Learn whether the specified annotation type should be ignored.
+ * @param clazz
+ * @return boolean
+ */
+ public boolean getDefaultIgnoreAnnotation(Class<?> clazz) {
+ return ignoreAnnotationDefaults.containsKey(clazz)
+ && ignoreAnnotationDefaults.get(clazz);
+ }
+
+ /**
+ * Ignore annotations on a particular {@link Member} of a class.
+ * @param member
+ */
+ public void setIgnoreAnnotationsOnMember(Member member) {
+ Class<?> beanClass = member.getDeclaringClass();
+ List<Member> memberList = ignoreAnnotationOnMember.get(beanClass);
+ if (memberList == null) {
+ memberList = new ArrayList<Member>();
+ ignoreAnnotationOnMember.put(beanClass, memberList);
+ }
+ memberList.add(member);
+ }
+
+ /**
+ * Learn whether annotations should be ignored on a particular {@link Member} of a class.
+ * @param member
+ * @return boolean
+ */
+ public boolean isIgnoreAnnotations(Member member) {
+ boolean ignoreAnnotation;
+ Class<?> clazz = member.getDeclaringClass();
+ List<Member> ignoreAnnotationForMembers = ignoreAnnotationOnMember.get(clazz);
+ if (ignoreAnnotationForMembers == null ||
+ !ignoreAnnotationForMembers.contains(member)) {
+ ignoreAnnotation = getDefaultIgnoreAnnotation(clazz);
+ } else {
+ ignoreAnnotation = ignoreAnnotationForMembers.contains(member);
+ }
+ if (ignoreAnnotation) {
+ logMessage(member, clazz);
+ }
+ return ignoreAnnotation;
+ }
+
+ private void logMessage(Member member, Class<?> clazz) {
+ String type;
+ if (member instanceof Field) {
+ type = "Field";
+ } else {
+ type = "Property";
+ }
+ log.log(Level.FINEST, String.format("%s level annotations are getting ignored for %s.%s", type, clazz.getName(), member.getName()));
+ }
+
+ /**
+ * Record the ignore state of a particular class.
+ * @param clazz
+ * @param b
+ */
+ public void setIgnoreAnnotationsOnClass(Class<?> clazz, boolean b) {
+ ignoreAnnotationOnClass.put(clazz, b);
+ }
+
+ /**
+ * Learn whether annotations should be ignored for a given class.
+ * @param clazz to check
+ * @return boolean
+ */
+ public boolean isIgnoreAnnotations(Class<?> clazz) {
+ boolean ignoreAnnotation;
+ if (ignoreAnnotationOnClass.containsKey(clazz)) {
+ ignoreAnnotation = ignoreAnnotationOnClass.get(clazz);
+ } else {
+ ignoreAnnotation = getDefaultIgnoreAnnotation(clazz);
+ }
+ if (ignoreAnnotation) {
+ log.log(Level.FINEST, String.format("Class level annotation are getting ignored for %s", clazz.getName()));
+ }
+ return ignoreAnnotation;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxy.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxy.java
new file mode 100644
index 0000000..ea95f78
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxy.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.bval.jsr303.util.SecureActions;
+
+/**
+ * Description: <br/>
+ * InvocationHandler implementation of <code>Annotation</code> that pretends it
+ * is a "real" source code annotation.
+ * <p/>
+ */
+// TODO move this guy up to org.apache.bval.jsr303 or
+// org.apache.bval.jsr303.model
+class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ private final Class<? extends Annotation> annotationType;
+ private final Map<String, Object> values;
+
+ /**
+ * Create a new AnnotationProxy instance.
+ *
+ * @param <A>
+ * @param descriptor
+ */
+ public <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> descriptor) {
+ this.annotationType = descriptor.getType();
+ values = getAnnotationValues(descriptor);
+ }
+
+ private <A extends Annotation> Map<String, Object> getAnnotationValues(AnnotationProxyBuilder<A> descriptor) {
+ Map<String, Object> result = new HashMap<String, Object>();
+ int processedValuesFromDescriptor = 0;
+ final Method[] declaredMethods = doPrivileged(
+ SecureActions.getDeclaredMethods(annotationType)
+ );
+ for (Method m : declaredMethods) {
+ if (descriptor.contains(m.getName())) {
+ result.put(m.getName(), descriptor.getValue(m.getName()));
+ processedValuesFromDescriptor++;
+ } else if (m.getDefaultValue() != null) {
+ result.put(m.getName(), m.getDefaultValue());
+ } else {
+ throw new IllegalArgumentException("No value provided for " + m.getName());
+ }
+ }
+ if (processedValuesFromDescriptor != descriptor.size()) {
+ throw new RuntimeException("Trying to instanciate " + annotationType + " with unknown paramters.");
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (values.containsKey(method.getName())) {
+ return values.get(method.getName());
+ }
+ return method.invoke(this, args);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Class<? extends Annotation> annotationType() {
+ return annotationType;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ result.append('@').append(annotationType().getName()).append('(');
+ boolean comma = false;
+ for (String m : getMethodsSorted()) {
+ if (comma)
+ result.append(", ");
+ result.append(m).append('=').append(values.get(m));
+ comma = true;
+ }
+ result.append(")");
+ return result.toString();
+ }
+
+ private SortedSet<String> getMethodsSorted() {
+ SortedSet<String> result = new TreeSet<String>();
+ result.addAll(values.keySet());
+ return result;
+ }
+
+
+
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxyBuilder.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxyBuilder.java
new file mode 100644
index 0000000..75cbf4a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxyBuilder.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr303.util.SecureActions;
+
+/**
+ * Description: Holds the information and creates an annotation proxy during xml
+ * parsing of validation mapping constraints. <br/>
+ */
+// TODO move this guy up to org.apache.bval.jsr303 or
+// org.apache.bval.jsr303.model
+final public class AnnotationProxyBuilder<A extends Annotation> {
+ private final Class<A> type;
+ private final Map<String, Object> elements = new HashMap<String, Object>();
+
+ /**
+ * Create a new AnnotationProxyBuilder instance.
+ *
+ * @param annotationType
+ */
+ public AnnotationProxyBuilder(Class<A> annotationType) {
+ this.type = annotationType;
+ }
+
+ /**
+ * Create a new AnnotationProxyBuilder instance.
+ *
+ * @param annotationType
+ * @param elements
+ */
+ public AnnotationProxyBuilder(Class<A> annotationType, Map<String, Object> elements) {
+ this(annotationType);
+ for (Map.Entry<String, Object> entry : elements.entrySet()) {
+ this.elements.put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Create a builder initially configured to create an annotation equivalent
+ * to <code>annot</code>.
+ *
+ * @param annot Annotation to be replicated.
+ */
+ @SuppressWarnings("unchecked")
+ public AnnotationProxyBuilder(A annot) {
+ this((Class<A>) annot.annotationType());
+ // Obtain the "elements" of the annotation
+ final Method[] methods = doPrivileged(SecureActions.getDeclaredMethods(annot.annotationType()));
+ for (Method m : methods) {
+ if (!m.isAccessible()) {
+ m.setAccessible(true);
+ }
+ try {
+ Object value = m.invoke(annot);
+ this.elements.put(m.getName(), value);
+ } catch (IllegalArgumentException e) {
+ // No args, so should not happen
+ throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+ } catch (IllegalAccessException e) {
+ throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+ } catch (InvocationTargetException e) {
+ throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+ }
+ }
+ }
+
+ /**
+ * Add an element to the configuration.
+ *
+ * @param elementName
+ * @param value
+ */
+ public void putValue(String elementName, Object value) {
+ elements.put(elementName, value);
+ }
+
+ /**
+ * Get the specified element value from the current configuration.
+ *
+ * @param elementName
+ * @return Object value
+ */
+ public Object getValue(String elementName) {
+ return elements.get(elementName);
+ }
+
+ /**
+ * Learn whether a given element has been configured.
+ *
+ * @param elementName
+ * @return <code>true</code> if an <code>elementName</code> element is found
+ * on this annotation
+ */
+ public boolean contains(String elementName) {
+ return elements.containsKey(elementName);
+ }
+
+ /**
+ * Get the number of configured elements.
+ *
+ * @return int
+ */
+ public int size() {
+ return elements.size();
+ }
+
+ /**
+ * Get the configured Annotation type.
+ *
+ * @return Class<A>
+ */
+ public Class<A> getType() {
+ return type;
+ }
+
+ /**
+ * Configure the well-known JSR303 "message" element.
+ *
+ * @param message
+ */
+ public void setMessage(String message) {
+ ConstraintAnnotationAttributes.MESSAGE.put(elements, message);
+ }
+
+ /**
+ * Configure the well-known JSR303 "groups" element.
+ *
+ * @param groups
+ */
+ public void setGroups(Class<?>[] groups) {
+ ConstraintAnnotationAttributes.GROUPS.put(elements, groups);
+ }
+
+ /**
+ * Configure the well-known JSR303 "payload" element.
+ *
+ * @param payload
+ */
+ public void setPayload(Class<? extends Payload>[] payload) {
+ ConstraintAnnotationAttributes.PAYLOAD.put(elements, payload);
+ }
+
+ /**
+ * Create the annotation represented by this builder.
+ *
+ * @return {@link Annotation}
+ */
+ public A createAnnotation() {
+ ClassLoader classLoader = SecureActions.getClassLoader(getType());
+ @SuppressWarnings("unchecked")
+ final Class<A> proxyClass = (Class<A>) Proxy.getProxyClass(classLoader, getType());
+ final InvocationHandler handler = new AnnotationProxy(this);
+ return doPrivileged(new PrivilegedAction<A>() {
+ public A run() {
+ try {
+ Constructor<A> constructor = proxyClass.getConstructor(InvocationHandler.class);
+ return constructor.newInstance(handler);
+ } catch (Exception e) {
+ throw new ValidationException("Unable to create annotation for configured constraint", e);
+ }
+ }
+ });
+ }
+
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/MetaConstraint.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/MetaConstraint.java
new file mode 100644
index 0000000..d4a6285
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/MetaConstraint.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+
+import javax.validation.ValidationException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * Description: hold parsed information from xml to complete MetaBean later<br/>
+ */
+//TODO move this guy up to org.apache.bval.jsr303 or org.apache.bval.jsr303.model
+//to decouple ApacheValidatorFactory from xml package and allow others to consume MetaConstraint
+public class MetaConstraint<T, A extends Annotation> {
+
+ /** The member the constraint was defined on. */
+ private final Member member;
+
+ /** The class of the bean hosting this constraint. */
+ private final Class<T> beanClass;
+
+ /** constraint annotation (proxy) */
+ private final A annotation;
+
+ private final AccessStrategy accessStrategy;
+
+ /**
+ * Create a new MetaConstraint instance.
+ * @param beanClass The class in which the constraint is defined on
+ * @param member The member on which the constraint is defined on, {@code null} if it is a class constraint}
+ * @param annotation
+ */
+ public MetaConstraint(Class<T> beanClass, Member member, A annotation) {
+ this.member = member;
+ this.beanClass = beanClass;
+ this.annotation = annotation;
+ if (member != null) {
+ accessStrategy = createAccessStrategy(member);
+ if (accessStrategy == null || accessStrategy.getPropertyName() ==
+ null) { // can happen if method does not follow the bean convention
+ throw new ValidationException(
+ "Annotated method does not follow the JavaBeans naming convention: " +
+ member);
+ }
+ } else {
+ this.accessStrategy = null;
+ }
+ }
+
+ private static AccessStrategy createAccessStrategy(Member member) {
+ if (member instanceof Method) {
+ return new MethodAccess((Method) member);
+ } else if (member instanceof Field) {
+ return new FieldAccess((Field) member);
+ } else {
+ return null; // class level
+ }
+ }
+
+ /**
+ * Get the bean class of this constraint.
+ * @return Class
+ */
+ public Class<T> getBeanClass() {
+ return beanClass;
+ }
+
+ /**
+ * Get the member to which this constraint applies.
+ * @return Member
+ */
+ public Member getMember() {
+ return member;
+ }
+
+ /**
+ * Get the annotation that defines this constraint.
+ * @return Annotation
+ */
+ public A getAnnotation() {
+ return annotation;
+ }
+
+ /**
+ * Get the access strategy used for the associated property.
+ * @return {@link AccessStrategy}
+ */
+ public AccessStrategy getAccessStrategy() {
+ return accessStrategy;
+ }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationMappingParser.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationMappingParser.java
new file mode 100644
index 0000000..69273fb
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationMappingParser.java
@@ -0,0 +1,568 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr303.util.EnumerationConverter;
+import org.apache.bval.jsr303.util.IOUtils;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+import org.apache.commons.beanutils.ConvertUtils;
+import org.apache.commons.beanutils.Converter;
+import org.apache.commons.lang3.StringUtils;
+
+
+/**
+ * Uses JAXB to parse constraints.xml based on validation-mapping-1.0.xsd.<br>
+ */
+@SuppressWarnings("restriction")
+public class ValidationMappingParser {
+ // private static final Log log = LogFactory.getLog(ValidationMappingParser.class);
+ private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.0.xsd";
+
+ private static final Set<ConstraintAnnotationAttributes> RESERVED_PARAMS = Collections.unmodifiableSet(EnumSet.of(
+ ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
+ ConstraintAnnotationAttributes.PAYLOAD));
+
+ private final Set<Class<?>> processedClasses;
+ private final ApacheValidatorFactory factory;
+
+ /**
+ * Create a new ValidationMappingParser instance.
+ * @param factory
+ */
+ public ValidationMappingParser(ApacheValidatorFactory factory) {
+ this.factory = factory;
+ this.processedClasses = new HashSet<Class<?>>();
+ }
+
+ /**
+ * Parse files with constraint mappings and collect information in the factory.
+ *
+ * @param xmlStreams - one or more contraints.xml file streams to parse
+ */
+ public void processMappingConfig(Set<InputStream> xmlStreams) throws ValidationException {
+ for (InputStream xmlStream : xmlStreams) {
+ ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
+
+ String defaultPackage = mapping.getDefaultPackage();
+ processConstraintDefinitions(mapping.getConstraintDefinition(), defaultPackage);
+ for (BeanType bean : mapping.getBean()) {
+ Class<?> beanClass = loadClass(bean.getClazz(), defaultPackage);
+ if (!processedClasses.add(beanClass)) {
+ // spec: A given class must not be described more than once amongst all
+ // the XML mapping descriptors.
+ throw new ValidationException(
+ beanClass.getName() + " has already be configured in xml.");
+ }
+ factory.getAnnotationIgnores()
+ .setDefaultIgnoreAnnotation(beanClass, bean.isIgnoreAnnotations());
+ processClassLevel(bean.getClassType(), beanClass, defaultPackage);
+ processFieldLevel(bean.getField(), beanClass, defaultPackage);
+ processPropertyLevel(bean.getGetter(), beanClass, defaultPackage);
+ processedClasses.add(beanClass);
+ }
+ }
+ }
+
+ /** @param in XML stream to parse using the validation-mapping-1.0.xsd */
+ private ConstraintMappingsType parseXmlMappings(InputStream in) {
+ ConstraintMappingsType mappings;
+ try {
+ JAXBContext jc = JAXBContext.newInstance(ConstraintMappingsType.class);
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ unmarshaller.setSchema(getSchema());
+ StreamSource stream = new StreamSource(in);
+ JAXBElement<ConstraintMappingsType> root =
+ unmarshaller.unmarshal(stream, ConstraintMappingsType.class);
+ mappings = root.getValue();
+ } catch (JAXBException e) {
+ throw new ValidationException("Failed to parse XML deployment descriptor file.",
+ e);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ return mappings;
+ }
+
+ /** @return validation-mapping-1.0.xsd based schema */
+ private Schema getSchema() {
+ return ValidationParser.getSchema(VALIDATION_MAPPING_XSD);
+ }
+
+ private void processClassLevel(ClassType classType, Class<?> beanClass,
+ String defaultPackage) {
+ if (classType == null) {
+ return;
+ }
+
+ // ignore annotation
+ if (classType.isIgnoreAnnotations() != null) {
+ factory.getAnnotationIgnores()
+ .setIgnoreAnnotationsOnClass(beanClass, classType.isIgnoreAnnotations());
+ }
+
+ // group sequence
+ Class<?>[] groupSequence =
+ createGroupSequence(classType.getGroupSequence(), defaultPackage);
+ if (groupSequence != null) {
+ factory.addDefaultSequence(beanClass, groupSequence);
+ }
+
+ // constraints
+ for (ConstraintType constraint : classType.getConstraint()) {
+ MetaConstraint<?, ?> metaConstraint =
+ createConstraint(constraint, beanClass, null, defaultPackage);
+ factory.addMetaConstraint(beanClass, metaConstraint);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <A extends Annotation, T> MetaConstraint<?, ?> createConstraint(
+ ConstraintType constraint, Class<T> beanClass, Member member,
+ String defaultPackage) {
+ Class<A> annotationClass =
+ (Class<A>) loadClass(constraint.getAnnotation(), defaultPackage);
+ AnnotationProxyBuilder<A> annoBuilder = new AnnotationProxyBuilder<A>(annotationClass);
+
+ if (constraint.getMessage() != null) {
+ annoBuilder.setMessage(constraint.getMessage());
+ }
+ annoBuilder.setGroups(getGroups(constraint.getGroups(), defaultPackage));
+ annoBuilder.setPayload(getPayload(constraint.getPayload(), defaultPackage));
+
+ for (ElementType elementType : constraint.getElement()) {
+ String name = elementType.getName();
+ checkValidName(name);
+ Class<?> returnType = getAnnotationParameterType(annotationClass, name);
+ Object elementValue = getElementValue(elementType, returnType, defaultPackage);
+ annoBuilder.putValue(name, elementValue);
+ }
+ return new MetaConstraint<T, A>(beanClass, member, annoBuilder.createAnnotation());
+ }
+
+ private void checkValidName(String name) {
+ for (ConstraintAnnotationAttributes attr : RESERVED_PARAMS) {
+ if (attr.getAttributeName().equals(name)) {
+ throw new ValidationException(name + " is a reserved parameter name.");
+ }
+ }
+ }
+
+ private <A extends Annotation> Class<?> getAnnotationParameterType(
+ final Class<A> annotationClass, final String name) {
+ final Method m = doPrivileged(SecureActions.getPublicMethod(annotationClass, name));
+ if (m == null) {
+ throw new ValidationException("Annotation of type " + annotationClass.getName() +
+ " does not contain a parameter " + name + ".");
+ }
+ return m.getReturnType();
+ }
+
+ private Object getElementValue(ElementType elementType, Class<?> returnType,
+ String defaultPackage) {
+ removeEmptyContentElements(elementType);
+
+ boolean isArray = returnType.isArray();
+ if (!isArray) {
+ if (elementType.getContent().size() != 1) {
+ throw new ValidationException(
+ "Attempt to specify an array where single value is expected.");
+ }
+ return getSingleValue(elementType.getContent().get(0), returnType, defaultPackage);
+ } else {
+ List<Object> values = new ArrayList<Object>();
+ for (Serializable s : elementType.getContent()) {
+ values.add(getSingleValue(s, returnType.getComponentType(), defaultPackage));
+ }
+ return values.toArray(
+ (Object[]) Array.newInstance(returnType.getComponentType(), values.size()));
+ }
+ }
+
+ private void removeEmptyContentElements(ElementType elementType) {
+ List<Serializable> contentToDelete = new ArrayList<Serializable>();
+ for (Serializable content : elementType.getContent()) {
+ if (content instanceof String && ((String) content).matches("[\\n ].*")) {
+ contentToDelete.add(content);
+ }
+ }
+ elementType.getContent().removeAll(contentToDelete);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object getSingleValue(Serializable serializable, Class<?> returnType,
+ String defaultPackage) {
+
+ Object returnValue;
+ if (serializable instanceof String) {
+ String value = (String) serializable;
+ returnValue = convertToResultType(returnType, value, defaultPackage);
+ } else if (serializable instanceof JAXBElement<?> &&
+ ((JAXBElement<?>) serializable).getDeclaredType()
+ .equals(String.class)) {
+ JAXBElement<?> elem = (JAXBElement<?>) serializable;
+ String value = (String) elem.getValue();
+ returnValue = convertToResultType(returnType, value, defaultPackage);
+ } else if (serializable instanceof JAXBElement<?> &&
+ ((JAXBElement<?>) serializable).getDeclaredType()
+ .equals(AnnotationType.class)) {
+ JAXBElement<?> elem = (JAXBElement<?>) serializable;
+ AnnotationType annotationType = (AnnotationType) elem.getValue();
+ try {
+ Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) returnType;
+ returnValue =
+ createAnnotation(annotationType, annotationClass, defaultPackage);
+ } catch (ClassCastException e) {
+ throw new ValidationException("Unexpected parameter value");
+ }
+ } else {
+ throw new ValidationException("Unexpected parameter value");
+ }
+ return returnValue;
+
+ }
+
+ private Object convertToResultType(Class<?> returnType, String value,
+ String defaultPackage) {
+ /**
+ * Class is represented by the fully qualified class name of the class.
+ * spec: Note that if the raw string is unqualified,
+ * default package is taken into account.
+ */
+ if (returnType.equals(Class.class)) {
+ value = toQualifiedClassName(value, defaultPackage);
+ }
+
+ /* Converter lookup */
+ Converter converter = ConvertUtils.lookup(returnType);
+ if (converter == null && returnType.isEnum()) {
+ converter = EnumerationConverter.getInstance();
+ }
+
+ if (converter != null) {
+ return converter.convert(returnType, value);
+ } else {
+ return converter;
+ }
+ }
+
+ private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType,
+ Class<A> returnType,
+ String defaultPackage) {
+ AnnotationProxyBuilder<A> metaAnnotation = new AnnotationProxyBuilder<A>(returnType);
+ for (ElementType elementType : annotationType.getElement()) {
+ String name = elementType.getName();
+ Class<?> parameterType = getAnnotationParameterType(returnType, name);
+ Object elementValue = getElementValue(elementType, parameterType, defaultPackage);
+ metaAnnotation.putValue(name, elementValue);
+ }
+ return metaAnnotation.createAnnotation();
+ }
+
+ private Class<?>[] getGroups(GroupsType groupsType, String defaultPackage) {
+ if (groupsType == null) {
+ return new Class[]{};
+ }
+
+ List<Class<?>> groupList = new ArrayList<Class<?>>();
+ for (JAXBElement<String> groupClass : groupsType.getValue()) {
+ groupList.add(loadClass(groupClass.getValue(), defaultPackage));
+ }
+ return groupList.toArray(new Class[groupList.size()]);
+ }
+
+
+ @SuppressWarnings("unchecked")
+ private Class<? extends Payload>[] getPayload(PayloadType payloadType,
+ String defaultPackage) {
+ if (payloadType == null) {
+ return new Class[]{};
+ }
+
+ List<Class<? extends Payload>> payloadList = new ArrayList<Class<? extends Payload>>();
+ for (JAXBElement<String> groupClass : payloadType.getValue()) {
+ Class<?> payload = loadClass(groupClass.getValue(), defaultPackage);
+ if (!Payload.class.isAssignableFrom(payload)) {
+ throw new ValidationException("Specified payload class " + payload.getName() +
+ " does not implement javax.validation.Payload");
+ } else {
+ payloadList.add((Class<? extends Payload>) payload);
+ }
+ }
+ return payloadList.toArray(new Class[payloadList.size()]);
+ }
+
+ private Class<?>[] createGroupSequence(GroupSequenceType groupSequenceType,
+ String defaultPackage) {
+ if (groupSequenceType != null) {
+ Class<?>[] groupSequence = new Class<?>[groupSequenceType.getValue().size()];
+ int i=0;
+ for (JAXBElement<String> groupName : groupSequenceType.getValue()) {
+ Class<?> group = loadClass(groupName.getValue(), defaultPackage);
+ groupSequence[i++] = group;
+ }
+ return groupSequence;
+ } else {
+ return null;
+ }
+ }
+
+ private void processFieldLevel(List<FieldType> fields, Class<?> beanClass,
+ String defaultPackage) {
+ List<String> fieldNames = new ArrayList<String>();
+ for (FieldType fieldType : fields) {
+ String fieldName = fieldType.getName();
+ if (fieldNames.contains(fieldName)) {
+ throw new ValidationException(fieldName +
+ " is defined more than once in mapping xml for bean " +
+ beanClass.getName());
+ } else {
+ fieldNames.add(fieldName);
+ }
+ final Field field = doPrivileged(SecureActions.getDeclaredField(beanClass, fieldName));
+ if (field == null) {
+ throw new ValidationException(
+ beanClass.getName() + " does not contain the fieldType " + fieldName);
+ }
+
+ // ignore annotations
+ boolean ignoreFieldAnnotation = fieldType.isIgnoreAnnotations() == null ? false :
+ fieldType.isIgnoreAnnotations();
+ if (ignoreFieldAnnotation) {
+ factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(field);
+ }
+
+ // valid
+ if (fieldType.getValid() != null) {
+ factory.addValid(beanClass, new FieldAccess(field));
+ }
+
+ // constraints
+ for (ConstraintType constraintType : fieldType.getConstraint()) {
+ MetaConstraint<?, ?> constraint =
+ createConstraint(constraintType, beanClass, field, defaultPackage);
+ factory.addMetaConstraint(beanClass, constraint);
+ }
+ }
+ }
+
+ private void processPropertyLevel(List<GetterType> getters, Class<?> beanClass,
+ String defaultPackage) {
+ List<String> getterNames = new ArrayList<String>();
+ for (GetterType getterType : getters) {
+ String getterName = getterType.getName();
+ if (getterNames.contains(getterName)) {
+ throw new ValidationException(getterName +
+ " is defined more than once in mapping xml for bean " +
+ beanClass.getName());
+ } else {
+ getterNames.add(getterName);
+ }
+ final Method method = getGetter(beanClass, getterName);
+ if (method == null) {
+ throw new ValidationException(
+ beanClass.getName() + " does not contain the property " + getterName);
+ }
+
+ // ignore annotations
+ boolean ignoreGetterAnnotation = getterType.isIgnoreAnnotations() == null ? false :
+ getterType.isIgnoreAnnotations();
+ if (ignoreGetterAnnotation) {
+ factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(method);
+ }
+
+ // valid
+ if (getterType.getValid() != null) {
+ factory.addValid(beanClass, new MethodAccess(getterName, method));
+ }
+
+ // constraints
+ for (ConstraintType constraintType : getterType.getConstraint()) {
+ MetaConstraint<?, ?> metaConstraint =
+ createConstraint(constraintType, beanClass, method, defaultPackage);
+ factory.addMetaConstraint(beanClass, metaConstraint);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void processConstraintDefinitions(
+ List<ConstraintDefinitionType> constraintDefinitionList, String defaultPackage) {
+ for (ConstraintDefinitionType constraintDefinition : constraintDefinitionList) {
+ String annotationClassName = constraintDefinition.getAnnotation();
+
+ Class<?> clazz = loadClass(annotationClassName, defaultPackage);
+ if (!clazz.isAnnotation()) {
+ throw new ValidationException(annotationClassName + " is not an annotation");
+ }
+ Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) clazz;
+
+ ValidatedByType validatedByType = constraintDefinition.getValidatedBy();
+ List<Class<? extends ConstraintValidator<?, ?>>> classes =
+ new ArrayList<Class<? extends ConstraintValidator<?, ?>>>();
+ /*
+ If include-existing-validator is set to false,
+ ConstraintValidator defined on the constraint annotation are ignored.
+ */
+ if (validatedByType.isIncludeExistingValidators() != null &&
+ validatedByType.isIncludeExistingValidators()) {
+ /*
+ If set to true, the list of ConstraintValidators described in XML
+ are concatenated to the list of ConstraintValidator described on the
+ annotation to form a new array of ConstraintValidator evaluated.
+ */
+ classes.addAll(findConstraintValidatorClasses(annotationClass));
+ }
+ for (JAXBElement<String> validatorClassName : validatedByType.getValue()) {
+ Class<? extends ConstraintValidator<?, ?>> validatorClass;
+ validatorClass = (Class<? extends ConstraintValidator<?, ?>>)
+ loadClass(validatorClassName.getValue());
+
+
+ if (!ConstraintValidator.class.isAssignableFrom(validatorClass)) {
+ throw new ValidationException(
+ validatorClass + " is not a constraint validator class");
+ }
+
+ /*
+ Annotation based ConstraintValidator come before XML based
+ ConstraintValidator in the array. The new list is returned
+ by ConstraintDescriptor.getConstraintValidatorClasses().
+ */
+ if (!classes.contains(validatorClass)) classes.add(validatorClass);
+ }
+ if (factory.getConstraintsCache().containsConstraintValidator(annotationClass)) {
+ throw new ValidationException("Constraint validator for " +
+ annotationClass.getName() + " already configured.");
+ } else {
+ factory.getConstraintsCache().putConstraintValidator(annotationClass,
+ classes.toArray(new Class[classes.size()]));
+ }
+ }
+ }
+
+ private List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> findConstraintValidatorClasses(
+ Class<? extends Annotation> annotationType) {
+ List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> classes =
+ new ArrayList<Class<? extends ConstraintValidator<? extends Annotation, ?>>>();
+
+ Class<? extends ConstraintValidator<?, ?>>[] validator =
+ factory.getDefaultConstraints().getValidatorClasses(annotationType);
+ if (validator != null) {
+ classes
+ .addAll(Arrays.asList(validator));
+ } else {
+ Class<? extends ConstraintValidator<?, ?>>[] validatedBy = annotationType
+ .getAnnotation(Constraint.class)
+ .validatedBy();
+ classes.addAll(Arrays.asList(validatedBy));
+ }
+ return classes;
+ }
+
+ private Class<?> loadClass(String className, String defaultPackage) {
+ return loadClass(toQualifiedClassName(className, defaultPackage));
+ }
+
+ private String toQualifiedClassName(String className, String defaultPackage) {
+ if (!isQualifiedClass(className)) {
+ className = defaultPackage + "." + className;
+ }
+ return className;
+ }
+
+ private boolean isQualifiedClass(String clazz) {
+ return clazz.contains(".");
+ }
+
+
+
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+
+
+ private static Method getGetter(final Class<?> clazz, final String propertyName) {
+ return doPrivileged(new PrivilegedAction<Method>() {
+ public Method run() {
+ try {
+ final String p = StringUtils.capitalize(propertyName);
+ try {
+ return clazz.getMethod("get" + p);
+ } catch (NoSuchMethodException e) {
+ return clazz.getMethod("is" + p);
+ }
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+ });
+
+ }
+
+
+
+ private Class<?> loadClass(final String className) {
+ ClassLoader loader = doPrivileged(SecureActions.getContextClassLoader());
+ if (loader == null)
+ loader = getClass().getClassLoader();
+
+ try {
+ return Class.forName(className, true, loader);
+ } catch (ClassNotFoundException ex) {
+ throw new ValidationException("Unable to load class: " + className, ex);
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationParser.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationParser.java
new file mode 100644
index 0000000..83accb2
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationParser.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+
+import org.apache.bval.jsr303.ConfigurationImpl;
+import org.apache.bval.jsr303.util.IOUtils;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.util.PrivilegedActions;
+import org.xml.sax.SAXException;
+
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.ValidationException;
+import javax.validation.spi.ValidationProvider;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: uses jaxb to parse validation.xml<br/>
+ */
+@SuppressWarnings("restriction")
+public class ValidationParser {
+ private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml";
+ private static final String VALIDATION_CONFIGURATION_XSD =
+ "META-INF/validation-configuration-1.0.xsd";
+ private static final Logger log = Logger.getLogger(ValidationParser.class.getName());
+ protected final String validationXmlFile;
+
+ /**
+ * Create a new ValidationParser instance.
+ *
+ * @param file
+ */
+ public ValidationParser(String file) {
+ if (file == null) {
+ validationXmlFile = DEFAULT_VALIDATION_XML_FILE;
+ } else {
+ validationXmlFile = file;
+ }
+ }
+
+ /**
+ * Process the validation configuration into <code>targetConfig</code>.
+ *
+ * @param targetConfig
+ */
+ public void processValidationConfig(ConfigurationImpl targetConfig) {
+ ValidationConfigType xmlConfig = parseXmlConfig();
+ if (xmlConfig != null) {
+ applyConfig(xmlConfig, targetConfig);
+ }
+ }
+
+ private ValidationConfigType parseXmlConfig() {
+ InputStream inputStream = null;
+ try {
+ inputStream = getInputStream(validationXmlFile);
+ if (inputStream == null) {
+ log.log(Level.FINEST, String.format("No %s found. Using annotation based configuration only.", validationXmlFile));
+ return null;
+ }
+
+ log.log(Level.FINEST, String.format("%s found.", validationXmlFile));
+
+ Schema schema = getSchema();
+ JAXBContext jc = JAXBContext.newInstance(ValidationConfigType.class);
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ unmarshaller.setSchema(schema);
+ StreamSource stream = new StreamSource(inputStream);
+ JAXBElement<ValidationConfigType> root =
+ unmarshaller.unmarshal(stream, ValidationConfigType.class);
+ return root.getValue();
+ } catch (JAXBException e) {
+ throw new ValidationException("Unable to parse " + validationXmlFile, e);
+ } catch (IOException e) {
+ throw new ValidationException("Unable to parse " + validationXmlFile, e);
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+
+ protected InputStream getInputStream(String path) throws IOException {
+ ClassLoader loader = PrivilegedActions.getClassLoader(getClass());
+ InputStream inputStream = loader.getResourceAsStream(path);
+
+ if (inputStream != null) {
+ // spec says: If more than one META-INF/validation.xml file
+ // is found in the classpath, a ValidationException is raised.
+ Enumeration<URL> urls = loader.getResources(path);
+ if (urls.hasMoreElements()) {
+ String url = urls.nextElement().toString();
+ while (urls.hasMoreElements()) {
+ if (!url.equals(urls.nextElement().toString())) { // complain when first duplicate found
+ throw new ValidationException("More than one " + path + " is found in the classpath");
+ }
+ }
+ }
+ }
+
+ return inputStream;
+ }
+
+ private Schema getSchema() {
+ return getSchema(VALIDATION_CONFIGURATION_XSD);
+ }
+
+ /**
+ * Get a Schema object from the specified resource name.
+ *
+ * @param xsd
+ * @return {@link Schema}
+ */
+ static Schema getSchema(String xsd) {
+ ClassLoader loader = PrivilegedActions.getClassLoader(ValidationParser.class);
+ SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ URL schemaUrl = loader.getResource(xsd);
+ try {
+ return sf.newSchema(schemaUrl);
+ } catch (SAXException e) {
+ log.log(Level.WARNING, String.format("Unable to parse schema: %s", xsd), e);
+ return null;
+ }
+ }
+
+ private void applyConfig(ValidationConfigType xmlConfig, ConfigurationImpl targetConfig) {
+ applyProviderClass(xmlConfig, targetConfig);
+ applyMessageInterpolator(xmlConfig, targetConfig);
+ applyTraversableResolver(xmlConfig, targetConfig);
+ applyConstraintFactory(xmlConfig, targetConfig);
+ applyMappingStreams(xmlConfig, targetConfig);
+ applyProperties(xmlConfig, targetConfig);
+ }
+
+ private void applyProperties(ValidationConfigType xmlConfig, ConfigurationImpl target) {
+ for (PropertyType property : xmlConfig.getProperty()) {
+ if (log.isLoggable(Level.FINEST)) {
+ log.log(Level.FINEST, String.format("Found property '%s' with value '%s' in %s", property.getName(), property.getValue(), validationXmlFile));
+ }
+ target.addProperty(property.getName(), property.getValue());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void applyProviderClass(ValidationConfigType xmlConfig, ConfigurationImpl target) {
+ String providerClassName = xmlConfig.getDefaultProvider();
+ if (providerClassName != null) {
+ Class<? extends ValidationProvider<?>> clazz =
+ (Class<? extends ValidationProvider<?>>) loadClass(providerClassName);
+ target.setProviderClass(clazz);
+ log.log(Level.INFO, String.format("Using %s as validation provider.", providerClassName));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void applyMessageInterpolator(ValidationConfigType xmlConfig,
+ ConfigurationImpl target) {
+ String messageInterpolatorClass = xmlConfig.getMessageInterpolator();
+ if (target.getMessageInterpolator() == null) {
+ if (messageInterpolatorClass != null) {
+ Class<MessageInterpolator> clazz = (Class<MessageInterpolator>)
+ loadClass(messageInterpolatorClass);
+ target.messageInterpolator(newInstance(clazz));
+ log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass));
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void applyTraversableResolver(ValidationConfigType xmlConfig,
+ ConfigurationImpl target) {
+ String traversableResolverClass = xmlConfig.getTraversableResolver();
+ if (target.getTraversableResolver() == null) {
+ if (traversableResolverClass != null) {
+ Class<TraversableResolver> clazz = (Class<TraversableResolver>)
+ loadClass(traversableResolverClass);
+ target.traversableResolver(newInstance(clazz));
+ log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass));
+ }
+ }
+ }
+
+ private <T> T newInstance(final Class<T> cls) {
+ return AccessController.doPrivileged(new PrivilegedAction<T>() {
+ public T run() {
+ try {
+ return cls.newInstance();
+ } catch (final Exception ex) {
+ throw new ValidationException("Cannot instantiate : " + cls, ex);
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ private void applyConstraintFactory(ValidationConfigType xmlConfig,
+ ConfigurationImpl target) {
+ String constraintFactoryClass = xmlConfig.getConstraintValidatorFactory();
+ if (target.getConstraintValidatorFactory() == null) {
+ if (constraintFactoryClass != null) {
+ Class<ConstraintValidatorFactory> clazz = (Class<ConstraintValidatorFactory>)
+ loadClass(constraintFactoryClass);
+ target.constraintValidatorFactory(newInstance(clazz));
+ log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass));
+ }
+ }
+ }
+
+ private void applyMappingStreams(ValidationConfigType xmlConfig,
+ ConfigurationImpl target) {
+ for (JAXBElement<String> mappingFileNameElement : xmlConfig.getConstraintMapping()) {
+ String mappingFileName = mappingFileNameElement.getValue();
+ if (mappingFileName.startsWith("/")) {
+ // Classloader needs a path without a starting /
+ mappingFileName = mappingFileName.substring(1);
+ }
+ log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName));
+ InputStream in = null;
+ try {
+ in = getInputStream(mappingFileName);
+ if (in == null) {
+ throw new ValidationException(
+ "Unable to open input stream for mapping file " +
+ mappingFileName);
+ }
+ } catch (IOException e) {
+ throw new ValidationException("Unable to open input stream for mapping file " +
+ mappingFileName, e);
+ }
+ target.addMapping(in);
+ }
+ }
+
+
+ private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(action);
+ } else {
+ return action.run();
+ }
+ }
+
+ private Class<?> loadClass(final String className) {
+ ClassLoader loader = doPrivileged(SecureActions.getContextClassLoader());
+ if (loader == null)
+ loader = getClass().getClassLoader();
+
+ try {
+ return Class.forName(className, true, loader);
+ } catch (ClassNotFoundException ex) {
+ throw new ValidationException("Unable to load class: " + className, ex);
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/main/resources/META-INF/services/javax.validation.spi.ValidationProvider b/trunk/bval-jsr303/src/main/resources/META-INF/services/javax.validation.spi.ValidationProvider
new file mode 100644
index 0000000..af55120
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/META-INF/services/javax.validation.spi.ValidationProvider
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.apache.bval.jsr303.ApacheValidationProvider
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/DefaultConstraints.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/DefaultConstraints.properties
new file mode 100644
index 0000000..6ed1e94
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/DefaultConstraints.properties
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# configuration for mapping of javax.validation.constraints to ConstraintValidator implementations
+# the annotations from the JSR303 validation-api are mentioned here, but you could
+# also extend the configuration to get a default implementation for other constraint-annotations.
+# format: key=constraint-annotation-class value=constraint-validator-class (multiple classes: comma-separated)
+
+javax.validation.constraints.AssertFalse=org.apache.bval.constraints.AssertFalseValidator
+javax.validation.constraints.AssertTrue=org.apache.bval.constraints.AssertTrueValidator
+javax.validation.constraints.DecimalMax=org.apache.bval.constraints.DecimalMaxValidatorForNumber,\
+ org.apache.bval.constraints.DecimalMaxValidatorForString
+javax.validation.constraints.DecimalMin=org.apache.bval.constraints.DecimalMinValidatorForNumber,\
+ org.apache.bval.constraints.DecimalMinValidatorForString
+javax.validation.constraints.Digits=org.apache.bval.constraints.DigitsValidatorForNumber,\
+ org.apache.bval.constraints.DigitsValidatorForString
+javax.validation.constraints.Future=org.apache.bval.constraints.FutureValidatorForDate,\
+ org.apache.bval.constraints.FutureValidatorForCalendar
+javax.validation.constraints.Max=org.apache.bval.constraints.MaxValidatorForNumber,\
+ org.apache.bval.constraints.MaxValidatorForString
+javax.validation.constraints.Min=org.apache.bval.constraints.MinValidatorForNumber,\
+ org.apache.bval.constraints.MinValidatorForString
+javax.validation.constraints.NotNull=org.apache.bval.constraints.NotNullValidator
+javax.validation.constraints.Null=org.apache.bval.constraints.NullValidator
+javax.validation.constraints.Past=org.apache.bval.constraints.PastValidatorForDate,\
+ org.apache.bval.constraints.PastValidatorForCalendar
+javax.validation.constraints.Size=org.apache.bval.constraints.SizeValidatorForString,\
+ org.apache.bval.constraints.SizeValidatorForMap,\
+ org.apache.bval.constraints.SizeValidatorForCollection,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfBoolean,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfByte,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfChar,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfDouble,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfFloat,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfInt,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfLong,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfObject,\
+ org.apache.bval.constraints.SizeValidatorForArrayOfShort
+javax.validation.constraints.Pattern=org.apache.bval.constraints.PatternValidator
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages.properties
new file mode 100644
index 0000000..5e4fd97
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages.properties
@@ -0,0 +1,37 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=must be null
+javax.validation.constraints.NotNull.message=may not be null
+javax.validation.constraints.AssertTrue.message=must be true
+javax.validation.constraints.AssertFalse.message=must be false
+javax.validation.constraints.Min.message=must be greater than or equal to {value}
+javax.validation.constraints.Max.message=must be less than or equal to {value}
+javax.validation.constraints.Size.message=size must be between {min} and {max}
+javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
+javax.validation.constraints.Past.message=must be a past date
+javax.validation.constraints.Future.message=must be a future date
+javax.validation.constraints.Pattern.message=must match the following regular expression: {regexp}
+javax.validation.constraints.DecimalMax.message=must be less than or equal to {value}
+javax.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=may not be empty
+org.apache.bval.constraints.Email.message=not a well-formed email address
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_de.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_de.properties
new file mode 100644
index 0000000..d55a8fd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_de.properties
@@ -0,0 +1,37 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=muss "null" sein
+javax.validation.constraints.NotNull.message=darf nicht "null" sein
+javax.validation.constraints.AssertTrue.message=muss "true" sein
+javax.validation.constraints.AssertFalse.message=muss "false" sein
+javax.validation.constraints.Min.message=muss gr\u00F6\u00DFer oder gleich {value} sein
+javax.validation.constraints.Max.message=muss kleiner oder gleich {value} sein
+javax.validation.constraints.Size.message=Gr\u00F6\u00DFe muss zwischen {min} und {max} liegen
+javax.validation.constraints.Digits.message=numerischer Wert au\u00DFerhalb des G\u00FCltigkeitsbereiches (erwarte: <{integer} digits>.<{fraction} digits>)
+javax.validation.constraints.Past.message=muss Datum in der Vergangenheit sein
+javax.validation.constraints.Future.message=muss Datum in der Zukunft sein
+javax.validation.constraints.Pattern.message=Muss mit regul\u00E4rem Ausdruck \u00FCbereinstimmen: {regexp}
+javax.validation.constraints.DecimalMax.message=muss kleiner oder gleich {value} sein
+javax.validation.constraints.DecimalMin.message=muss gr\u00F6\u00DFer oder gleich {value} sein
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=darf nicht leer sein
+org.apache.bval.constraints.Email.message=muss g\u00FCltiges Format einer EMail-Adresse sein
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_en.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_en.properties
new file mode 100644
index 0000000..4ecfad3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_en.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# intentionally empty, because english messages are located in the default bundle.
+# this file ensures, that a lookup with an english locale uses the default bundle and not the
+# bundle of the default locale, which is platform specific
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_es.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_es.properties
new file mode 100644
index 0000000..97e1761
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_es.properties
@@ -0,0 +1,37 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=tiene que ser null
+javax.validation.constraints.NotNull.message=no puede ser null
+javax.validation.constraints.AssertTrue.message=tiene que ser true
+javax.validation.constraints.AssertFalse.message=tiene que ser false
+javax.validation.constraints.Min.message=tiene que ser menor o igual que {value}
+javax.validation.constraints.Max.message=tiene que ser mayor o igual que {value}
+javax.validation.constraints.Size.message=el tamaño tiene que estar comprendido entre {min} y {max}
+javax.validation.constraints.Digits.message=valor numérico fuera de los límites (se espera <{integer} cifras>.<{fraction} cifras>)
+javax.validation.constraints.Past.message=tiene que ser una fecha en el pasado
+javax.validation.constraints.Future.message=tiene que ser una fecha en el futuro
+javax.validation.constraints.Pattern.message=tiene que corresponder a la expresión regular "{regexp}"
+javax.validation.constraints.DecimalMax.message=tiene que ser mayor o igual que {value}
+javax.validation.constraints.DecimalMin.message=tiene que ser menor o igual que {value}
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=no puede ser vacio
+org.apache.bval.constraints.Email.message=tiene que ser un correo email bien formado
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_it.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_it.properties
new file mode 100644
index 0000000..87e3273
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_it.properties
@@ -0,0 +1,37 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=deve essere null
+javax.validation.constraints.NotNull.message=non pu\u00F2 essere null
+javax.validation.constraints.AssertTrue.message=deve essere true
+javax.validation.constraints.AssertFalse.message=deve essere false
+javax.validation.constraints.Min.message=deve essere minore o uguale di {value}
+javax.validation.constraints.Max.message=deve essere maggiore o uguale di {value}
+javax.validation.constraints.Size.message=le dimensioni devono essere tra {min} e {max}
+javax.validation.constraints.Digits.message=valore numerico fuori dai limiti (atteso <{integer} cifre>.<{fraction} cifre>)
+javax.validation.constraints.Past.message=deve essere una data nel passato
+javax.validation.constraints.Future.message=deve essere una data futura
+javax.validation.constraints.Pattern.message=deve corrispondere all'espressione regolare "{regexp}"
+javax.validation.constraints.DecimalMax.message=deve essere maggiore o uguale di {value}
+javax.validation.constraints.DecimalMin.message=deve essere minore o uguale di {value}
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=non pu\u00F2 essere vuoto
+org.apache.bval.constraints.Email.message=non \u00E8 un indirizzo email ben formato
diff --git a/trunk/bval-jsr303/src/main/xjb/binding-customization.xjb b/trunk/bval-jsr303/src/main/xjb/binding-customization.xjb
new file mode 100644
index 0000000..2e76014
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/xjb/binding-customization.xjb
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
+ <jxb:bindings schemaLocation="../xsd/validation-mapping-1.0.xsd" node="/xs:schema">
+ <jxb:globalBindings>
+ <xjc:javaType name="java.lang.String" xmlType="xs:string"
+ adapter="javax.xml.bind.annotation.adapters.CollapsedStringAdapter"/>
+ </jxb:globalBindings>
+
+ <jxb:bindings node="//xs:complexType[9]/xs:sequence[1]/xs:element[1]">
+ <jxb:property name="classType"/>
+ </jxb:bindings>
+ </jxb:bindings>
+</jxb:bindings>
diff --git a/trunk/bval-jsr303/src/main/xsd/validation-configuration-1.0.xsd b/trunk/bval-jsr303/src/main/xsd/validation-configuration-1.0.xsd
new file mode 100644
index 0000000..5476714
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/xsd/validation-configuration-1.0.xsd
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xs:schema attributeFormDefault="unqualified"
+ elementFormDefault="qualified"
+ targetNamespace="http://jboss.org/xml/ns/javax/validation/configuration"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ version="1.0">
+ <xs:element name="validation-config" type="config:validation-configType"
+ xmlns:config="http://jboss.org/xml/ns/javax/validation/configuration"/>
+ <xs:complexType name="validation-configType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="default-provider" minOccurs="0"/>
+ <xs:element type="xs:string" name="message-interpolator" minOccurs="0"/>
+ <xs:element type="xs:string" name="traversable-resolver" minOccurs="0"/>
+ <xs:element type="xs:string" name="constraint-validator-factory" minOccurs="0"/>
+ <xs:element type="xs:string" name="constraint-mapping" maxOccurs="unbounded" minOccurs="0"/>
+ <xs:element type="config:propertyType" name="property" maxOccurs="unbounded" minOccurs="0"
+ xmlns:config="http://jboss.org/xml/ns/javax/validation/configuration"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="propertyType">
+ <xs:simpleContent>
+ <xs:extension base="xs:string">
+ <xs:attribute name="name" use="required" type="xs:string"/>
+ </xs:extension>
+ </xs:simpleContent>
+ </xs:complexType>
+</xs:schema>
diff --git a/trunk/bval-jsr303/src/main/xsd/validation-mapping-1.0.xsd b/trunk/bval-jsr303/src/main/xsd/validation-mapping-1.0.xsd
new file mode 100644
index 0000000..87c34b9
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/xsd/validation-mapping-1.0.xsd
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xs:schema attributeFormDefault="unqualified"
+ elementFormDefault="qualified"
+ targetNamespace="http://jboss.org/xml/ns/javax/validation/mapping"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ version="1.0">
+ <xs:element name="constraint-mappings"
+ type="map:constraint-mappingsType"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+
+ <xs:complexType name="payloadType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groupsType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="groupSequenceType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="constraint-mappingsType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="default-package" minOccurs="0"/>
+ <xs:element type="map:beanType"
+ name="bean"
+ maxOccurs="unbounded"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ <xs:element type="map:constraint-definitionType"
+ name="constraint-definition"
+ maxOccurs="unbounded"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="validated-byType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="include-existing-validators" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="constraintType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="message" minOccurs="0"/>
+ <xs:element type="map:groupsType"
+ name="groups"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ <xs:element type="map:payloadType"
+ name="payload"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ <xs:element type="map:elementType"
+ name="element"
+ maxOccurs="unbounded"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="annotation" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="elementType" mixed="true">
+ <xs:sequence>
+ <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+ <xs:element type="map:annotationType"
+ name="annotation"
+ maxOccurs="unbounded"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="classType">
+ <xs:sequence>
+ <xs:element type="map:groupSequenceType"
+ name="group-sequence"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ maxOccurs="unbounded"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="beanType">
+ <xs:sequence>
+ <xs:element type="map:classType"
+ name="class"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping">
+ </xs:element>
+ <xs:element type="map:fieldType"
+ name="field"
+ minOccurs="0"
+ maxOccurs="unbounded"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ <xs:element type="map:getterType"
+ name="getter"
+ minOccurs="0"
+ maxOccurs="unbounded"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="class" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="annotationType">
+ <xs:sequence>
+ <xs:element type="map:elementType"
+ name="element"
+ maxOccurs="unbounded"
+ minOccurs="0"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ </xs:complexType>
+ <xs:complexType name="getterType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+ <xs:complexType name="constraint-definitionType">
+ <xs:sequence>
+ <xs:element type="map:validated-byType"
+ name="validated-by"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="annotation" use="required"/>
+ </xs:complexType>
+ <xs:complexType name="fieldType">
+ <xs:sequence>
+ <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+ <xs:element type="map:constraintType"
+ name="constraint"
+ minOccurs="0"
+ maxOccurs="unbounded"
+ xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+ </xs:sequence>
+ <xs:attribute type="xs:string" name="name" use="required"/>
+ <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+ </xs:complexType>
+</xs:schema>
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertFalseValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertFalseValidatorTest.java
new file mode 100644
index 0000000..4677d34
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertFalseValidatorTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link AssertFalseValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must be false.</li>
+ * <li><code>null</code> elements are considered valid.</li>
+ * </ul>
+ *
+ * TODO: Mock context and verify that it's not used during validation.
+ *
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.4"
+ *
+ * @author Carlos Vara
+ */
+public class AssertFalseValidatorTest extends TestCase {
+
+ public static Test suite() {
+ return new TestSuite(AssertFalseValidatorTest.class);
+ }
+
+ public AssertFalseValidatorTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test {@link AssertFalseValidator} with <code>null</code> context.
+ */
+ public void testAssertFalseValidator() {
+ AssertFalseValidator afv = new AssertFalseValidator();
+ assertFalse("True value validation must fail", afv.isValid(true, null));
+ assertTrue("False value validation must succeed", afv.isValid(false, null));
+ assertTrue("Null value validation must succeed", afv.isValid(null, null));
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertTrueValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertTrueValidatorTest.java
new file mode 100644
index 0000000..016fe02
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertTrueValidatorTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link AssertTrueValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must be true.</li>
+ * <li><code>null</code> elements are considered valid.</li>
+ * </ul>
+ *
+ * TODO: Mock context and verify that it's not used during validation.
+ *
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.3"
+ *
+ * @author Carlos Vara
+ */
+public class AssertTrueValidatorTest extends TestCase {
+
+ public static Test suite() {
+ return new TestSuite(AssertTrueValidatorTest.class);
+ }
+
+ public AssertTrueValidatorTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test {@link AssertTrueValidator} with null context.
+ */
+ public void testAssertTrueValidator() {
+ AssertTrueValidator atv = new AssertTrueValidator();
+ assertTrue("True value validation must succeed", atv.isValid(true, null));
+ assertFalse("False value validation must fail", atv.isValid(false, null));
+ assertTrue("Null value validation must succeed", atv.isValid(null, null));
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AuthorAddressKnown.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AuthorAddressKnown.java
new file mode 100644
index 0000000..4240b5d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AuthorAddressKnown.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.constraints;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Payload;
+
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Author;
+
+/**
+ *
+ *
+ * @version $Rev: 999729 $ $Date: 2010-09-21 21:37:54 -0500 (Tue, 21 Sep 2010) $
+ */
+@Target( { ANNOTATION_TYPE, METHOD, FIELD })
+@Constraint(validatedBy = AuthorAddressKnown.Validator.class)
+@Retention(RUNTIME)
+public @interface AuthorAddressKnown {
+
+ String message() default "{org.apache.bval.constraints.AuthorAddressKnown.message}";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default { };
+
+ public static class Validator implements ConstraintValidator<AuthorAddressKnown, Author> {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void initialize(AuthorAddressKnown constraintAnnotation) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid(Author value, ConstraintValidatorContext context) {
+ if (value.getAddresses() == null) {
+ return false;
+ }
+ for (Address address : value.getAddresses()) {
+ if (address != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/CompanyEmail.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/CompanyEmail.java
new file mode 100644
index 0000000..154cd2f
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/CompanyEmail.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Pattern.List({
+ // email
+ @Pattern(regexp = "[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}"),
+ // agimatec
+ @Pattern(regexp = ".*?COMPANY.*?")
+})
+/**
+ * test a constraint WITHOUT an own ConstraintValidator implementation.
+ * the validations, that must be processed are in the combined constraints only!!
+ * the @Constraint annotation is nevertheless required so that the framework searches
+ * for combined constraints.
+ */
+@Constraint(validatedBy = {})
+@Documented
+@Target({ANNOTATION_TYPE, METHOD, FIELD, CONSTRUCTOR, PARAMETER})
+@Retention(RUNTIME)
+public @interface CompanyEmail {
+ String message() default "Not an email of COMPANY";
+
+ @OverridesAttribute(constraint = Pattern.class, name = "message",
+ constraintIndex = 0) String emailMessage() default "Not an email";
+
+ @OverridesAttribute(constraint = Pattern.class, name = "message",
+ constraintIndex = 1) String agimatecMessage() default "Not COMPANY";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+
+ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
+ @Retention(RUNTIME)
+ @Documented
+ @interface List {
+ CompanyEmail[] value();
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DecimalMinMaxValidatorsTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DecimalMinMaxValidatorsTest.java
new file mode 100644
index 0000000..d0e0bb8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DecimalMinMaxValidatorsTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.TestCase;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import java.util.Set;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 06.04.2010<br>
+ * Time: 13:45:09<br>
+ */
+public class DecimalMinMaxValidatorsTest extends TestCase {
+
+ @DecimalMin("922392239223.06")
+ public double dmin;
+ @DecimalMax("922392239223.09")
+ public double dmax;
+
+ public void testDecimalMinValue() {
+ Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+ this.dmin = 922392239223.05;
+ this.dmax = 922392239223.08;
+
+ Set<ConstraintViolation<DecimalMinMaxValidatorsTest>> res = v.validate(this);
+ assertFalse("Min validation failed", res.isEmpty());
+ }
+
+ public void testDecimalMaxValue() {
+ Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+ this.dmin = Double.MAX_VALUE;
+ this.dmax = 922392239223.1;
+
+ Set<ConstraintViolation<DecimalMinMaxValidatorsTest>> res = v.validate(this);
+ assertFalse("Max validation failed", res.isEmpty());
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DigitsValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DigitsValidatorTest.java
new file mode 100644
index 0000000..745ed2b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DigitsValidatorTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.math.BigDecimal;
+
+/**
+ * DigitsConstraintValidator Tester.
+ *
+ * @author <Authors name>
+ * @since <pre>02/03/2009</pre>
+ * @version 1.0
+ */
+public class DigitsValidatorTest extends TestCase {
+ public DigitsValidatorTest(String name) {
+ super(name);
+ }
+
+ public void testValidateNumber() {
+ DigitsValidatorForNumber validator = new DigitsValidatorForNumber();
+ validator.setFractional(4);
+ validator.setIntegral(2);
+ Assert.assertFalse(validator.isValid(new BigDecimal("100.1234"), null));
+ Assert.assertFalse(validator.isValid(new BigDecimal("99.12345"), null));
+ Assert.assertTrue(validator.isValid(new BigDecimal("99.1234"), null));
+ Assert.assertFalse(validator.isValid(Double.valueOf(100.1234), null));
+ Assert.assertFalse(validator.isValid(Double.valueOf(99.12345), null));
+ Assert.assertTrue(validator.isValid(Double.valueOf(99.1234), null));
+ Assert.assertTrue(validator.isValid(Double.valueOf(99.123400), null));
+ Assert.assertTrue(validator.isValid(new BigDecimal("99.123400"), null));
+ }
+
+ public void testValidateString() {
+ DigitsValidatorForString validator = new DigitsValidatorForString();
+ validator.setFractional(4);
+ validator.setIntegral(2);
+ String val = "100.12345";
+ Assert.assertFalse(validator.isValid(val, null));
+ val = "99.1234";
+ Assert.assertTrue(validator.isValid(val, null));
+ }
+
+ public void testValidateNumber2() {
+ DigitsValidatorForNumber validator = new DigitsValidatorForNumber();
+ validator.setFractional(4);
+ validator.setIntegral(2);
+ Long val = new Long("100");
+ Assert.assertFalse(validator.isValid(val, null));
+ val = new Long("99");
+ Assert.assertTrue(validator.isValid(val, null));
+ }
+
+ public void testValidateString2() {
+ DigitsValidatorForString validator = new DigitsValidatorForString();
+ validator.setFractional(0);
+ validator.setIntegral(2);
+ String val = "99.5";
+ Assert.assertFalse(validator.isValid(val, null));
+ val = "99";
+ Assert.assertTrue(validator.isValid(val, null));
+ }
+
+ public static Test suite() {
+ return new TestSuite(DigitsValidatorTest.class);
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/EmailValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/EmailValidatorTest.java
new file mode 100644
index 0000000..cbae43a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/EmailValidatorTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.example.Customer;
+
+import javax.validation.Validator;
+
+/**
+ * EmailValidator Tester.
+ *
+ * @author Roman Stumm
+ * @version 1.0
+ * @since <pre>10/14/2008</pre>
+ */
+public class EmailValidatorTest extends TestCase {
+ public static class EmailAddressBuilder {
+ @Email
+ private StringBuilder buffer = new StringBuilder();
+
+ /**
+ * Get the buffer.
+ * @return StringBuilder
+ */
+ public StringBuilder getBuffer() {
+ return buffer;
+ }
+
+ }
+
+ private Validator validator;
+
+ public EmailValidatorTest(String name) {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = ApacheValidatorFactory.getDefault().getValidator();
+ }
+
+ public void testEmail() {
+ Customer customer = new Customer();
+ customer.setCustomerId("id-1");
+ customer.setFirstName("Mary");
+ customer.setLastName("Do");
+ customer.setPassword("12345");
+
+ Assert.assertEquals(0, validator.validate(customer).size());
+
+ customer.setEmailAddress("some@invalid@address");
+ Assert.assertEquals(1, validator.validate(customer).size());
+
+ customer.setEmailAddress("some.valid-012345@address_at-test.org");
+ Assert.assertEquals(0, validator.validate(customer).size());
+ }
+
+ public void testEmailCharSequence() {
+ EmailAddressBuilder emailAddressBuilder = new EmailAddressBuilder();
+ Assert.assertEquals(0, validator.validate(emailAddressBuilder).size());
+ emailAddressBuilder.getBuffer().append("foo");
+ Assert.assertEquals(1, validator.validate(emailAddressBuilder).size());
+ emailAddressBuilder.getBuffer().append('@');
+ Assert.assertEquals(1, validator.validate(emailAddressBuilder).size());
+ emailAddressBuilder.getBuffer().append("bar");
+ Assert.assertEquals(0, validator.validate(emailAddressBuilder).size());
+ emailAddressBuilder.getBuffer().append('.');
+ Assert.assertEquals(1, validator.validate(emailAddressBuilder).size());
+ emailAddressBuilder.getBuffer().append("baz");
+ Assert.assertEquals(0, validator.validate(emailAddressBuilder).size());
+ }
+
+ public static Test suite() {
+ return new TestSuite(EmailValidatorTest.class);
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipCode.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipCode.java
new file mode 100644
index 0000000..80e5b4e
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipCode.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: example for composed constraint.
+ * not implemented! simple dummy implemenation for tests only! <br/>
+ */
+@NotEmpty
+@NotNull
+@Size(min = 4, max = 5, message = "Zipcode should be of size {value}")
+@Constraint(validatedBy = FrenchZipcodeValidator.class)
+@ReportAsSingleViolation
+@Documented
+@Target({ANNOTATION_TYPE, METHOD, FIELD})
+@Retention(RUNTIME)
+public @interface FrenchZipCode {
+ @OverridesAttribute.List({
+ @OverridesAttribute(constraint = Size.class, name= "min"),
+ @OverridesAttribute(constraint = Size.class, name = "max")})
+ int size() default 6;
+
+ @OverridesAttribute(constraint=Size.class, name="message")
+ String sizeMessage() default "{error.zipcode.size}";
+
+ String message() default "Wrong zipcode";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
new file mode 100644
index 0000000..dc92142
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: not implemented (test only)<br/>
+ */
+public class FrenchZipcodeValidator implements ConstraintValidator<FrenchZipCode, Object> {
+ public void initialize(FrenchZipCode constraintAnnotation) {
+ // do nothing
+ }
+
+ public boolean isValid(Object object, ConstraintValidatorContext validationContext) {
+ return null != object;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasStringValidator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasStringValidator.java
new file mode 100644
index 0000000..7f2be60
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasStringValidator.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: <br/>
+ */
+public class HasStringValidator implements ConstraintValidator<HasValue, String> {
+ private String[] values;
+
+ public void initialize(HasValue stringValues) {
+ values = stringValues.value();
+ }
+
+ public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
+ return s == null || ArrayUtils.contains(values, s);
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasValue.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasValue.java
new file mode 100644
index 0000000..abc0125
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasValue.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: allow distinct string values for element (like enums) <br/>
+ */
+@Target({ANNOTATION_TYPE, METHOD, FIELD})
+@Constraint(validatedBy = {HasStringValidator.class})
+@Retention(RUNTIME)
+public @interface HasValue {
+ String[] value();
+
+ String message() default "Wrong value, must be one of {value}";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/MinMaxValidatorsForNumberTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/MinMaxValidatorsForNumberTest.java
new file mode 100644
index 0000000..e4f68dc
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/MinMaxValidatorsForNumberTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.TestCase;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.util.Set;
+
+/**
+ * Check correct behaviour of {@link MinValidatorForNumber} and
+ * {@link MaxValidatorForNumber} on boundary values.
+ * <p/>
+ * The chosen numbers: 9223372036854775806l and 9223372036854775807l cast to the
+ * same double value.
+ *
+ * @author Carlos Vara
+ */
+public class MinMaxValidatorsForNumberTest extends TestCase {
+
+ @Min(value = 9223372036854775807l)
+ public long min;
+
+ @Max(value = 9223372036854775806l)
+ public long max;
+
+ public void testMinBoundaryValue() {
+ Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+ this.min = 9223372036854775806l;
+ this.max = 0l;
+
+ // Current min value is smaller, should fail, but it doesn't
+ Set<ConstraintViolation<MinMaxValidatorsForNumberTest>> res = v.validate(this);
+ assertFalse("Min validation failed", res.isEmpty());
+ }
+
+ public void testMaxBoundaryValue() {
+ Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+ this.min = Long.MAX_VALUE;
+ this.max = 9223372036854775807l;
+
+ // Current max value is bigger, should fail, but it doesn't
+ Set<ConstraintViolation<MinMaxValidatorsForNumberTest>> res = v.validate(this);
+ assertFalse("Max validation failed", res.isEmpty());
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NotNullValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NotNullValidatorTest.java
new file mode 100644
index 0000000..a48150d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NotNullValidatorTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link NotNullValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must not be null.</li>
+ * </ul>
+ *
+ * TODO: Mock context and verify that it's not used during validation.
+ *
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.2"
+ *
+ * @author Carlos Vara
+ */
+public class NotNullValidatorTest extends TestCase {
+
+ public static Test suite() {
+ return new TestSuite(NotNullValidatorTest.class);
+ }
+
+ public NotNullValidatorTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test {@link NotNullValidator} with null context.
+ */
+ public void testNotNullValidator() {
+ NotNullValidator nnv = new NotNullValidator();
+ assertTrue("Non null value validation must succeed", nnv.isValid("hello", null));
+ assertFalse("Null value validation must fail", nnv.isValid(null, null));
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NullValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NullValidatorTest.java
new file mode 100644
index 0000000..7cfe281
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NullValidatorTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link NullValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must be null.</li>
+ * </ul>
+ *
+ * TODO: Mock context and verify that it's not used during validation.
+ *
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.1"
+ *
+ * @author Carlos Vara
+ */
+public class NullValidatorTest extends TestCase {
+
+ public static Test suite() {
+ return new TestSuite(NullValidatorTest.class);
+ }
+
+ public NullValidatorTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test {@link AssertFalseValidator} with null context.
+ */
+ public void testNullValidator() {
+ NullValidator nv = new NullValidator();
+ assertTrue("Null value validation must succeed", nv.isValid(null, null));
+ assertFalse("Non null value validation must fail", nv.isValid("hello", null));
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java
new file mode 100644
index 0000000..0cb2fb9
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: <br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 12:02:06 <br/>
+ */
+@NotEmpty
+@NotNull
+@Size(min = 4, max = 5)
+@Retention(RUNTIME)
+@Constraint(validatedBy = {})
+// test that Password is validated although only combined constraints exists, no own implementation
+public @interface Password {
+ Class<?>[] groups() default {};
+
+ String message() default "Wrong password";
+
+ int robustness() default 8;
+
+ Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherence.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherence.java
new file mode 100644
index 0000000..6c92c7a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherence.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * not implemented! simple dummy implemenation for tests only!
+ */
+@Documented
+@Constraint(validatedBy = ZipCodeCityCoherenceValidator.class)
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface ZipCodeCityCoherence {
+ String message() default "{validator.zipCodeCityCoherence}";
+ Class<?>[] groups() default {};
+ Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherenceValidator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherenceValidator.java
new file mode 100644
index 0000000..92ab6d8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherenceValidator.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.constraints;
+
+
+import org.apache.bval.jsr303.example.ZipCodeCityCarrier;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: Class not implemented! simple dummy implemenation for tests only! <br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 11:45:22 <br/>
+ */
+public class ZipCodeCityCoherenceValidator
+ implements ConstraintValidator<ZipCodeCityCoherence, ZipCodeCityCarrier> {
+ public void initialize(ZipCodeCityCoherence constraintAnnotation) {
+ }
+
+ public boolean isValid(ZipCodeCityCarrier adr, ConstraintValidatorContext context) {
+ boolean r = true;
+ if ("error".equals(adr.getZipCode())) {
+ context.disableDefaultConstraintViolation();
+ context.buildConstraintViolationWithTemplate("zipcode not OK").addConstraintViolation();
+ r = false;
+ }
+ if ("error".equals(adr.getCity())) {
+ context.disableDefaultConstraintViolation();
+ context.buildConstraintViolationWithTemplate("city not OK").addNode("city").addConstraintViolation();
+ r = false;
+ }
+ return r;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BeanDescriptorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BeanDescriptorTest.java
new file mode 100644
index 0000000..c73c6a6
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BeanDescriptorTest.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+import javax.validation.metadata.*;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Tests the implementation of {@link BeanDescriptor} and its dependent
+ * interfaces.
+ *
+ * @author Carlos Vara
+ */
+public class BeanDescriptorTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ /**
+ * Check that groups(), message() and payload() are always in the
+ * attributes.
+ */
+ public void testMandatoryAttributesPresentInConstraintDescriptor() {
+ Set<ConstraintDescriptor<?>> nameDescriptors =
+ validator.getConstraintsForClass(Form.class).getConstraintsForProperty("name").getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 1, nameDescriptors.size());
+ ConstraintDescriptor<?> nameDescriptor = nameDescriptors.iterator().next();
+ Assert.assertTrue("groups attribute not present", nameDescriptor.getAttributes().containsKey("groups"));
+ Assert.assertTrue("payload attribute not present", nameDescriptor.getAttributes().containsKey("payload"));
+ Assert.assertTrue("message attribute not present", nameDescriptor.getAttributes().containsKey("message"));
+ }
+
+ /**
+ * Check that the groups() attribute value has the correct value when
+ * inheriting groups.
+ */
+ public void testCorrectValueForInheritedGroupsAttribute() {
+ Set<ConstraintDescriptor<?>> passwordDescriptors =
+ validator.getConstraintsForClass(Account.class).getConstraintsForProperty("password")
+ .getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 1, passwordDescriptors.size());
+ ConstraintDescriptor<?> passwordDescriptor = passwordDescriptors.iterator().next();
+ Assert.assertEquals("Incorrect number of composing constraints", 1, passwordDescriptor
+ .getComposingConstraints().size());
+ ConstraintDescriptor<?> notNullDescriptor = passwordDescriptor.getComposingConstraints().iterator().next();
+
+ // Check that the groups value containts Group1.class
+ Class<?>[] notNullGroups = (Class<?>[]) notNullDescriptor.getAttributes().get("groups");
+ boolean found = false;
+ for (Class<?> group : notNullGroups) {
+ if (group == Group1.class) {
+ found = true;
+ break;
+ }
+ }
+ Assert.assertTrue("Group1 not present in groups attribute", found);
+ }
+
+ /**
+ * Check that the groups() attribute value contains the correct interface as
+ * implicit group when the constraint is defined in that interface instead
+ * of the queried class.
+ */
+ public void testImplicitGroupIsPresent() {
+ Set<ConstraintDescriptor<?>> nameDescriptors =
+ validator.getConstraintsForClass(Woman.class).getConstraintsForProperty("name").getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 1, nameDescriptors.size());
+ ConstraintDescriptor<?> notNullDescriptor = nameDescriptors.iterator().next();
+
+ // Check that the groups attribute value contains the implicit group
+ // Person and the Default group
+ Class<?>[] notNullGroups = (Class<?>[]) notNullDescriptor.getAttributes().get("groups");
+ Assert.assertEquals("Incorrect number of groups", 2, notNullGroups.length);
+ Assert.assertTrue("Default group not present", notNullGroups[0].equals(Default.class)
+ || notNullGroups[1].equals(Default.class));
+ Assert.assertTrue("Implicit group not present", notNullGroups[0].equals(Person.class)
+ || notNullGroups[1].equals(Person.class));
+ }
+
+ /**
+ * Check that the groups() attribute value does not contain the implicit
+ * interface group when querying the interface directly.
+ */
+ public void testNoImplicitGroupWhenQueryingInterfaceDirectly() {
+ Set<ConstraintDescriptor<?>> nameDescriptors =
+ validator.getConstraintsForClass(Person.class).getConstraintsForProperty("name").getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 1, nameDescriptors.size());
+ ConstraintDescriptor<?> notNullDescriptor = nameDescriptors.iterator().next();
+
+ // Check that only the default group is present
+ Class<?>[] notNullGroups = (Class<?>[]) notNullDescriptor.getAttributes().get("groups");
+ Assert.assertEquals("Incorrect number of groups", 1, notNullGroups.length);
+ Assert.assertTrue("Default group not present", notNullGroups[0].equals(Default.class));
+ }
+
+ /**
+ * Check that the implementations of
+ * {@link ElementDescriptor#getElementClass()} work as defined in the spec.
+ */
+ public void testElementDescriptorGetElementClass() {
+ BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Person.class);
+ Assert.assertEquals("Incorrect class returned", Person.class, beanDescriptor.getElementClass());
+
+ PropertyDescriptor nameDescriptor = beanDescriptor.getConstraintsForProperty("name");
+ Assert.assertEquals("Incorrect class returned", String.class, nameDescriptor.getElementClass());
+ }
+
+ /**
+ * Check the correct behavior of
+ * {@link ConstraintFinder#lookingAt(javax.validation.metadata.Scope)}.
+ */
+ public void testConstraintFinderLookingAt() {
+ PropertyDescriptor nameDescriptor =
+ validator.getConstraintsForClass(Woman.class).getConstraintsForProperty("name");
+ Set<ConstraintDescriptor<?>> constraints =
+ nameDescriptor.findConstraints().lookingAt(Scope.HIERARCHY).getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 1, constraints.size());
+
+ constraints = nameDescriptor.findConstraints().lookingAt(Scope.LOCAL_ELEMENT).getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 0, constraints.size());
+ TestUtils.failOnModifiable(constraints, "constraintFinder constraintDescriptors");
+ //verify that changes to one ConstraintFinder don't affect the base:
+ constraints = nameDescriptor.getConstraintDescriptors();
+ Assert.assertEquals("Incorrect number of descriptors", 1, constraints.size());
+ }
+
+ public static class Form {
+ @NotNull
+ public String name;
+ }
+
+ public static class Account {
+ @Password(groups = { Group1.class })
+ public String password;
+ }
+
+ @NotNull(groups = {})
+ @Constraint(validatedBy = {})
+ @Documented
+ @Target( { METHOD, FIELD, TYPE })
+ @Retention(RUNTIME)
+ public static @interface Password {
+ String message() default "Invalid password";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static interface Group1 {
+ }
+
+ public static class Woman implements Person {
+
+ private String name;
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ public static interface Person {
+ @NotNull
+ String getName();
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BootstrapTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BootstrapTest.java
new file mode 100644
index 0000000..4745489
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BootstrapTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Configuration;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.ValidationProviderResolver;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.bootstrap.ProviderSpecificBootstrap;
+import javax.validation.spi.ValidationProvider;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.NotNullValidator;
+import org.apache.bval.jsr303.example.Customer;
+
+/**
+ * Description: <br/>
+ */
+public class BootstrapTest extends TestCase {
+ public void testDirectBootstrap() {
+ Validator validator = ApacheValidatorFactory.getDefault().getValidator();
+ Assert.assertNotNull(validator);
+ Assert.assertTrue(ApacheValidatorFactory.getDefault() == ApacheValidatorFactory.getDefault());
+ }
+
+ public void testEverydayBootstrap() {
+ ApacheValidatorFactory factory = (ApacheValidatorFactory) Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+ Assert.assertNotNull(validator);
+
+ // each call to Validation.getValidationBuilder() returns a new builder
+ // with new state
+ ApacheValidatorFactory factory2 = (ApacheValidatorFactory) Validation.buildDefaultValidatorFactory();
+ Assert.assertTrue(factory2 != factory);
+ Assert.assertTrue(factory2.getMessageInterpolator() != factory.getMessageInterpolator());
+
+ }
+
+ public void testLocalizedMessageInterpolatorFactory() {
+ Configuration<?> builder = Validation.byDefaultProvider().configure();
+ // changing the builder allows to create different factories
+ DefaultMessageInterpolator interpolator = new DefaultMessageInterpolator();
+ builder.messageInterpolator(interpolator);
+ ApacheValidatorFactory factory = (ApacheValidatorFactory) builder.buildValidatorFactory();
+
+ // ALTERNATIVE:
+ // you could do it without modifying the builder or reusing it,
+ // but then you need to use bval-core proprietary APIs:
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ // now factory's message resolver is using the english locale
+ }
+
+ /**
+ * some tests based on RI tested behaviors to ensure our implementation
+ * works as the reference implementation
+ */
+
+ public void testCustomConstraintFactory() {
+
+ Configuration<?> builder = Validation.byDefaultProvider().configure();
+ assertDefaultBuilderAndFactory(builder);
+
+ ValidatorFactory factory = builder.buildValidatorFactory();
+ Validator validator = factory.getValidator();
+
+ Customer customer = new Customer();
+ customer.setFirstName("John");
+
+ Set<ConstraintViolation<Customer>> ConstraintViolations = validator.validate(customer);
+ Assert.assertFalse(ConstraintViolations.isEmpty());
+
+ builder = Validation.byDefaultProvider().configure();
+ builder.constraintValidatorFactory(new ConstraintValidatorFactory() {
+ public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+ if (key == NotNullValidator.class) {
+ @SuppressWarnings("unchecked")
+ final T result = (T) new BadlyBehavedNotNullValidator();
+ return result;
+ }
+ return new DefaultConstraintValidatorFactory().getInstance(key);
+ }
+ });
+ factory = builder.buildValidatorFactory();
+ validator = factory.getValidator();
+ Set<ConstraintViolation<Customer>> ConstraintViolations2 = validator.validate(customer);
+ Assert.assertTrue("Wrong number of constraints", ConstraintViolations.size() > ConstraintViolations2.size());
+ }
+
+ public void testCustomResolverAndType() {
+ ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+ public List<ValidationProvider<?>> getValidationProviders() {
+ List<ValidationProvider<?>> list = new ArrayList<ValidationProvider<?>>(1);
+ list.add(new ApacheValidationProvider());
+ return list;
+ }
+ };
+
+ ApacheValidatorConfiguration builder =
+ Validation.byProvider(ApacheValidationProvider.class).providerResolver(resolver).configure();
+ assertDefaultBuilderAndFactory(builder);
+ }
+
+ public void testCustomResolver() {
+ ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+ public List<ValidationProvider<?>> getValidationProviders() {
+ return Collections.<ValidationProvider<?>> singletonList(new ApacheValidationProvider());
+ }
+ };
+
+ Configuration<?> builder = Validation.byDefaultProvider().providerResolver(resolver).configure();
+ assertDefaultBuilderAndFactory(builder);
+ }
+
+ private void assertDefaultBuilderAndFactory(Configuration<?> builder) {
+ Assert.assertNotNull(builder);
+ Assert.assertTrue(builder instanceof ConfigurationImpl);
+
+ ValidatorFactory factory = builder.buildValidatorFactory();
+ Assert.assertNotNull(factory);
+ Assert.assertTrue(factory instanceof ApacheValidatorFactory);
+ }
+
+ public void testFailingCustomResolver() {
+ ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+ public List<ValidationProvider<?>> getValidationProviders() {
+ return Collections.emptyList();
+ }
+ };
+
+ ProviderSpecificBootstrap<ApacheValidatorConfiguration> type =
+ Validation.byProvider(ApacheValidationProvider.class);
+
+ final ProviderSpecificBootstrap<ApacheValidatorConfiguration> specializedBuilderFactory =
+ type.providerResolver(resolver);
+
+ try {
+ specializedBuilderFactory.configure();
+ Assert.fail();
+ } catch (ValidationException e) {
+ Assert.assertTrue("Wrong error message", e.getMessage().contains("provider")
+ && e.getMessage().contains("org.apache.bval.jsr303.ApacheValidationProvider"));
+ }
+ }
+
+ class BadlyBehavedNotNullValidator extends NotNullValidator {
+ @Override
+ public boolean isValid(Object object, ConstraintValidatorContext context) {
+ return true;
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CircularReferencesTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CircularReferencesTest.java
new file mode 100644
index 0000000..5b8dd88
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CircularReferencesTest.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import javax.validation.*;
+import javax.validation.constraints.Size;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Checks that circular references in the bean graph are correctly detected when
+ * validating.
+ *
+ * @author Carlos Vara
+ */
+public class CircularReferencesTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ /**
+ * Checks that validation correctly stops when finding a circular
+ * dependency.
+ */
+ public void testAutoreferringBean() {
+ Person p1 = new Person();
+ p1.name = "too-long-name";
+ p1.sibling = p1;
+
+ Set<ConstraintViolation<Person>> violations = validator.validate(p1);
+
+ Assert.assertEquals("Only 1 violation should be reported", 1, violations.size());
+ ConstraintViolation<Person> violation = violations.iterator().next();
+ Assert.assertEquals("Incorrect violation path", "name", violation.getPropertyPath().toString());
+ }
+
+ /**
+ * Checks that a bean is always validated when appearing in non-circular
+ * paths inside the bean graph.
+ */
+ public void testNonCircularArrayOfSameBean() {
+ Boss boss = new Boss();
+ Person p1 = new Person();
+ p1.name = "too-long-name";
+
+ boss.employees = new Person[] { p1, p1, p1, p1 };
+
+ Set<ConstraintViolation<Boss>> violations = validator.validate(boss);
+
+ Assert.assertEquals("A total of 4 violations should be reported", 4, violations.size());
+ }
+
+ public static class Person {
+
+ @Valid
+ public Person sibling;
+
+ @Size(max = 10)
+ public String name;
+
+ }
+
+ public static class Boss {
+
+ @Valid
+ public Person[] employees;
+
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ComposedConstraintsTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ComposedConstraintsTest.java
new file mode 100644
index 0000000..538b9a1
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ComposedConstraintsTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Size;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.jsr303.example.CompanyAddress;
+import org.apache.bval.jsr303.example.FrenchAddress;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class ComposedConstraintsTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ public void testMetaDataAPI_ComposedConstraints() {
+ ElementDescriptor ed =
+ validator.getConstraintsForClass(FrenchAddress.class)
+ .getConstraintsForProperty("zipCode");
+ Assert.assertEquals(1, ed.getConstraintDescriptors().size());
+ for (ConstraintDescriptor<?> cd : ed.getConstraintDescriptors()) {
+ Assert.assertTrue(cd.isReportAsSingleViolation());
+ Assert.assertEquals(3, cd.getComposingConstraints().size());
+ Assert.assertTrue("no composing constraints found!!",
+ !cd.getComposingConstraints().isEmpty());
+ processConstraintDescriptor(cd); //check all constraints on zip code
+ }
+ }
+
+ public void processConstraintDescriptor(ConstraintDescriptor<?> cd) {
+ //Size.class is understood by the tool
+ if (cd.getAnnotation().annotationType().equals(Size.class)) {
+ @SuppressWarnings("unused")
+ Size m = (Size) cd.getAnnotation();//what for?
+ }
+ for (ConstraintDescriptor<?> composingCd : cd.getComposingConstraints()) {
+ //check composing constraints recursively
+ processConstraintDescriptor(composingCd);
+ }
+ }
+
+ public void testValidateComposed() {
+ FrenchAddress adr = new FrenchAddress();
+ Set<ConstraintViolation<FrenchAddress>> findings = validator.validate(adr);
+ Assert.assertEquals(1, findings.size()); // with @ReportAsSingleConstraintViolation
+
+ ConstraintViolation<FrenchAddress> finding = findings.iterator().next();
+ Assert.assertEquals("Wrong zipcode", finding.getMessage());
+
+ adr.setZipCode("1234567");
+ findings = validator.validate(adr);
+ Assert.assertEquals(0, findings.size());
+
+ adr.setZipCode("1234567234567");
+ findings = validator.validate(adr);
+ Assert.assertTrue(findings.size() > 0); // too long
+ }
+
+ public void testOverridesAttributeConstraintIndex() {
+ CompanyAddress adr = new CompanyAddress("invalid-string");
+ Set<ConstraintViolation<CompanyAddress>> findings = validator.validate(adr);
+ assertEquals(2, findings.size()); // without @ReportAsSingleConstraintViolation
+ assertNotNull(TestUtils.getViolationWithMessage(findings, "Not COMPANY"));
+ assertNotNull(TestUtils.getViolationWithMessage(findings, "Not an email"));
+
+ adr = new CompanyAddress("JOHN_DO@WEB.DE");
+ findings = validator.validate(adr);
+ assertEquals(1, findings.size());
+ assertNotNull(TestUtils.getViolationWithMessage(findings, "Not COMPANY"));
+
+ adr = new CompanyAddress("JOHN_DO@COMPANY.DE");
+ findings = validator.validate(adr);
+ Assert.assertTrue(findings.isEmpty());
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintCompositionTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintCompositionTest.java
new file mode 100644
index 0000000..fc24889
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintCompositionTest.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintViolation;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Checks that groups are correctly inherited from the root constraint to its
+ * compositing constraints.
+ *
+ * @author Carlos Vara
+ */
+public class ConstraintCompositionTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ /**
+ * Check correct group inheritance on constraint composition on a 1 level
+ * hierarchy.
+ */
+ public void test1LevelInheritance() {
+ Set<ConstraintViolation<Person>> violations = validator.validate(new Person());
+
+ Assert.assertEquals("Wrong number of violations detected", 1, violations.size());
+ String msg = violations.iterator().next().getMessage();
+ Assert.assertEquals("Incorrect violation message", "A person needs a non null name", msg);
+
+ violations = validator.validate(new Person(), Group1.class);
+ Assert.assertEquals("Wrong number of violations detected", 0, violations.size());
+ }
+
+ /**
+ * Check correct group inheritance on constraint composition on a 2 level
+ * hierarchy.
+ */
+ public void test2LevelInheritance() {
+ Set<ConstraintViolation<Man>> violations = validator.validate(new Man());
+
+ Assert.assertEquals("Wrong number of violations detected", 0, violations.size());
+
+ violations = validator.validate(new Man(), Group1.class);
+ Assert.assertEquals("Wrong number of violations detected", 1, violations.size());
+ String msg = violations.iterator().next().getMessage();
+ Assert.assertEquals("Incorrect violation message", "A person needs a non null name", msg);
+ }
+
+ /**
+ * Checks that the groups() value of the constraint annotations are
+ * correctly set to the inherited ones.
+ */
+ public void testAnnotationGroupsAreInherited() {
+ // Check that the groups() value is right when querying the metadata
+ ConstraintDescriptor<?> manNameDesc =
+ validator.getConstraintsForClass(Man.class).getConstraintsForProperty("name").getConstraintDescriptors()
+ .iterator().next();
+ ConstraintDescriptor<?> personNameDesc = manNameDesc.getComposingConstraints().iterator().next();
+ ConstraintDescriptor<?> notNullDesc = personNameDesc.getComposingConstraints().iterator().next();
+ Assert.assertEquals("There should only be 1 group", 1, manNameDesc.getGroups().size());
+ Assert.assertTrue("Group1 should be present", manNameDesc.getGroups().contains(Group1.class));
+ Assert.assertEquals("There should only be 1 group", 1, personNameDesc.getGroups().size());
+ Assert.assertTrue("Group1 should be present", personNameDesc.getGroups().contains(Group1.class));
+ Assert.assertEquals("There should only be 1 group", 1, personNameDesc.getGroups().size());
+ Assert.assertTrue("Group1 should be present", notNullDesc.getGroups().contains(Group1.class));
+
+ // Check that the groups() value is right when accessing it from an
+ // error
+ Set<ConstraintViolation<Man>> violations = validator.validate(new Man(), Group1.class);
+ Set<Class<?>> notNullGroups = violations.iterator().next().getConstraintDescriptor().getGroups();
+ Assert.assertEquals("There should only be 1 group", 1, notNullGroups.size());
+ Assert.assertTrue("Group1 should be the only group", notNullGroups.contains(Group1.class));
+ }
+
+ /**
+ * Checks that the payload() value of the constraint annotations are
+ * correctly set to the inherited ones.
+ */
+ public void testAnnotationPayloadsAreInherited() {
+ // Check that the payload() value is right when querying the metadata
+ ConstraintDescriptor<?> manNameDesc =
+ validator.getConstraintsForClass(Man.class).getConstraintsForProperty("name").getConstraintDescriptors()
+ .iterator().next();
+ ConstraintDescriptor<?> personNameDesc = manNameDesc.getComposingConstraints().iterator().next();
+ ConstraintDescriptor<?> notNullDesc = personNameDesc.getComposingConstraints().iterator().next();
+ Assert.assertEquals("There should only be 1 payload class", 1, manNameDesc.getPayload().size());
+ Assert.assertTrue("Payload1 should be present", manNameDesc.getPayload().contains(Payload1.class));
+ Assert.assertEquals("There should only be 1 payload class", 1, personNameDesc.getPayload().size());
+ Assert.assertTrue("Payload1 should be present", personNameDesc.getPayload().contains(Payload1.class));
+ Assert.assertEquals("There should only be 1 payload class", 1, personNameDesc.getPayload().size());
+ Assert.assertTrue("Payload1 should be present", notNullDesc.getPayload().contains(Payload1.class));
+
+ // Check that the payload() value is right when accessing it from an
+ // error
+ Set<ConstraintViolation<Man>> violations = validator.validate(new Man(), Group1.class);
+ Set<Class<? extends Payload>> notNullPayload =
+ violations.iterator().next().getConstraintDescriptor().getPayload();
+ Assert.assertEquals("There should only be 1 payload class", 1, notNullPayload.size());
+ Assert.assertTrue("Payload1 should be the only payload", notNullPayload.contains(Payload1.class));
+ }
+
+ /**
+ * Checks that {@link OverridesAttribute#constraintIndex()} parsing and
+ * applying works.
+ */
+ public void testIndexedOverridesAttributes() {
+ Person p = new Person();
+ p.name = "valid";
+
+ // With a valid id, no errors expected
+ p.id = "1234";
+ Set<ConstraintViolation<Person>> constraintViolations = validator.validate(p);
+ Assert.assertTrue("No violations should be reported on valid id", constraintViolations.isEmpty());
+
+ // With a short id, only 1 error expected
+ p.id = "1";
+ constraintViolations = validator.validate(p);
+ Assert.assertEquals("Only 1 violation expected", 1, constraintViolations.size());
+ ConstraintViolation<Person> violation = constraintViolations.iterator().next();
+ Assert.assertEquals("Wrong violation", "Id is too short", violation.getMessage());
+
+ // With a long id, only 1 error expected
+ p.id = "loooooong id";
+ constraintViolations = validator.validate(p);
+ Assert.assertEquals("Only 1 violation expected", 1, constraintViolations.size());
+ violation = constraintViolations.iterator().next();
+ Assert.assertEquals("Wrong violation", "Id is too long", violation.getMessage());
+ }
+
+ /**
+ * Checks that errors are reported correctly when using
+ * {@link ReportAsSingleViolation}.
+ */
+ public void testReportAsAsingleViolation() {
+ Code c = new Code();
+ c.code = "very invalid code";
+ Set<ConstraintViolation<Code>> constraintViolations = validator.validate(c);
+
+ // Only 1 error expected
+ Assert.assertEquals("Only 1 violation expected", 1, constraintViolations.size());
+ ConstraintViolation<Code> violation = constraintViolations.iterator().next();
+ Assert.assertEquals("Wrong violation message", "Invalid code", violation.getMessage());
+ Assert.assertEquals("Wrong violation type", ElevenDigitsCode.class, ((Annotation) violation
+ .getConstraintDescriptor().getAnnotation()).annotationType());
+ }
+
+ public static class Person {
+ @PersonName
+ String name;
+
+ @PersonId
+ String id;
+ }
+
+ public static class Man {
+ @ManName(groups = { Group1.class }, payload = { Payload1.class })
+ String name;
+ }
+
+ public static class Code {
+ @ElevenDigitsCode
+ String code;
+ }
+
+ @NotNull(message = "A person needs a non null name", groups = { Group1.class }, payload = {})
+ @Constraint(validatedBy = {})
+ @Documented
+ @Target( { METHOD, FIELD, TYPE })
+ @Retention(RUNTIME)
+ public static @interface PersonName {
+ String message() default "Wrong person name";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ @PersonName(groups = { Group2.class }, payload = { Payload1.class, Payload2.class })
+ @Constraint(validatedBy = {})
+ @Documented
+ @Target( { METHOD, FIELD, TYPE })
+ @Retention(RUNTIME)
+ public static @interface ManName {
+ String message() default "Wrong man name";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ @Size.List( { @Size(min = 3, max = 3, message = "Id is too short"),
+ @Size(min = 5, max = 5, message = "Id is too long") })
+ @Constraint(validatedBy = {})
+ @Documented
+ @Target( { METHOD, FIELD, TYPE })
+ @Retention(RUNTIME)
+ public static @interface PersonId {
+ String message() default "Wrong person id";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+
+ @OverridesAttribute(constraint = Size.class, constraintIndex = 0, name = "max")
+ int maxSize() default 1000;
+
+ @OverridesAttribute(constraint = Size.class, constraintIndex = 1, name = "min")
+ int minSize() default 0;
+ }
+
+ @Size(min = 11, max = 11)
+ @Pattern(regexp = "\\d*")
+ @Constraint(validatedBy = {})
+ @ReportAsSingleViolation
+ @Documented
+ @Target( { METHOD, FIELD, TYPE })
+ @Retention(RUNTIME)
+ public static @interface ElevenDigitsCode {
+ String message() default "Invalid code";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static interface Group1 {
+ }
+
+ public static interface Group2 {
+ }
+
+ public static class Payload1 implements Payload {
+ }
+
+ public static class Payload2 implements Payload {
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java
new file mode 100644
index 0000000..3729370
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Min;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.NotNullValidator;
+
+/**
+ * Checks the correct parsing of constraint definitions.
+ *
+ * @author Carlos Vara
+ */
+public class ConstraintDefinitionsTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ /**
+ * Checks the correct parsing of a constraint with an array of constraints
+ * as attributes.
+ */
+ public void testCustomAttributes() {
+ BeanDescriptor constraints = validator.getConstraintsForClass(Person.class);
+ Set<ConstraintDescriptor<?>> ageConstraints =
+ constraints.getConstraintsForProperty("age").getConstraintDescriptors();
+
+ Assert.assertEquals("There should be 2 constraints in 'age'", ageConstraints.size(), 2);
+ for (ConstraintDescriptor<?> cd : ageConstraints) {
+ Assert.assertEquals("Annotation should be @Min", cd.getAnnotation().annotationType().getName(), Min.class
+ .getName());
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with no <code>groups()</code> method.
+ */
+ public void testNoGroupsConstraint() {
+ try {
+ validator.validate(new NoGroups());
+ fail("No exception thrown when parsing a constraint definition with no groups() method");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with an invalid <code>groups()</code>
+ * method.
+ */
+ public void testInvalidDefaultGroupsConstraint() {
+ try {
+ validator.validate(new InvalidGroups());
+ fail("No exception thrown when parsing a constraint definition with a groups() method does not return Class[]");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with no <code>payload()</code> method.
+ */
+ public void testNoPayloadConstraint() {
+ try {
+ validator.validate(new NoPayload());
+ fail("No exception thrown when parsing a constraint definition with no payload() method");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with an invalid <code>payload()</code>
+ * method.
+ */
+ public void testInvalidDefaultPayloadConstraint() {
+ try {
+ validator.validate(new InvalidPayload());
+ fail("No exception thrown when parsing a constraint definition with a payload() method does not return an empty array");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with no <code>message()</code> method.
+ */
+ public void testNoMessageConstraint() {
+ try {
+ validator.validate(new NoMessage());
+ fail("No exception thrown when parsing a constraint definition with no payload() method");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with an invalid <code>message()</code>
+ * method.
+ */
+ public void testInvalidDefaultMessageConstraint() {
+ try {
+ validator.validate(new InvalidMessage());
+ fail("No exception thrown when parsing a constraint definition with a message() method does not return a String");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ /**
+ * Checks that a {@link ConstraintDefinitionException} is thrown when
+ * parsing a constraint definition with a method starting with 'valid'.
+ */
+ public void testInvalidAttributeConstraint() {
+ try {
+ validator.validate(new InvalidAttribute());
+ fail("No exception thrown when parsing a constraint definition with a method starting with 'valid'");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ public static class Person {
+ @MinList( { @Min(value = 20), @Min(value = 30) })
+ public Integer age;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ public static @interface MinList {
+ Min[] value();
+ }
+
+ public static class NoGroups {
+ @NoGroupsConstraint
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface NoGroupsConstraint {
+ String message() default "def msg";
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidGroups {
+ @InvalidGroupsConstraint
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface InvalidGroupsConstraint {
+ String message() default "def msg";
+
+ String[] groups() default { "Group1" };
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class NoPayload {
+ @NoPayloadConstraint
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface NoPayloadConstraint {
+ String message() default "def msg";
+
+ String[] groups() default {};
+ }
+
+ public static class InvalidPayload {
+ @InvalidPayloadConstraint
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface InvalidPayloadConstraint {
+ String message() default "def msg";
+
+ String[] groups() default {};
+
+ Class<? extends Payload>[] payload() default { Payload1.class };
+
+ public static class Payload1 implements Payload {
+ }
+ }
+
+ public static class NoMessage {
+ @NoMessageConstraint
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface NoMessageConstraint {
+ String[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidMessage {
+ @InvalidMessageConstraint(message = 2)
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface InvalidMessageConstraint {
+ int message();
+
+ String[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidAttribute {
+ @InvalidAttributeConstraint
+ public String prop;
+ }
+
+ @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ @Constraint(validatedBy = { NotNullValidator.class })
+ public static @interface InvalidAttributeConstraint {
+ String message() default "def msg";
+
+ String[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+
+ String validValue() default "1";
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java
new file mode 100644
index 0000000..771528c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.model.ValidationListener.Error;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder;
+
+/**
+ * Checks to validate the correct implementation of
+ * {@link ConstraintValidatorContext} and its sub-interfaces.
+ *
+ * @author Carlos Vara
+ */
+public class ConstraintValidatorContextTest extends TestCase {
+
+ private ConstraintValidatorContextImpl cvc;
+ private ConstraintViolationBuilder cvb;
+
+ @Mock
+ private GroupValidationContext<ValidationListener> groupValidationContext;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ Mockito.when(groupValidationContext.getPropertyPath()).thenAnswer(new Answer<PathImpl>() {
+
+ public PathImpl answer(InvocationOnMock invocation) throws Throwable {
+ return PathImpl.createPathFromString("");
+ }
+ });
+ this.cvc = new ConstraintValidatorContextImpl(groupValidationContext,
+ null);
+ this.cvc.disableDefaultConstraintViolation();
+ this.cvb = cvc.buildConstraintViolationWithTemplate("dummy.msg.tpl");
+ }
+
+ public void testPerson1() {
+ cvb.addNode("person").addNode(null).inIterable().atIndex(1)
+ .addConstraintViolation();
+ Error error = cvc.getErrorMessages().iterator().next();
+ PathImpl errorPath = (PathImpl) error.getOwner();
+ Assert.assertEquals("Incorrect path created", "person[1]", errorPath
+ .toString());
+ }
+
+ public void testPersonLawyerName() {
+ cvb.addNode("person").addNode("name").inIterable().atKey("john")
+ .addConstraintViolation();
+ Error error = cvc.getErrorMessages().iterator().next();
+ PathImpl errorPath = (PathImpl) error.getOwner();
+ Assert.assertEquals("Incorrect path created", "person[john].name",
+ errorPath.toString());
+ }
+
+ public void test0Name() {
+ cvb.addNode(null).addNode("name").inIterable().atIndex(0).addNode(null)
+ .inIterable().addConstraintViolation();
+ Error error = cvc.getErrorMessages().iterator().next();
+ PathImpl errorPath = (PathImpl) error.getOwner();
+ Assert.assertEquals("Incorrect path created", "[0].name[]", errorPath
+ .toString());
+ }
+
+ public void testEmptyIndex() {
+ cvb.addNode(null).addNode(null).inIterable().addConstraintViolation();
+ Error error = cvc.getErrorMessages().iterator().next();
+ PathImpl errorPath = (PathImpl) error.getOwner();
+ Assert.assertEquals("Incorrect path created", "[]", errorPath
+ .toString());
+ }
+
+ public void testRootPath() {
+ // Adding only nulls should still give a root path
+ cvb.addNode(null).addNode(null).addNode(null).addNode(null)
+ .addConstraintViolation();
+ Error error = cvc.getErrorMessages().iterator().next();
+ PathImpl errorPath = (PathImpl) error.getOwner();
+ Assert.assertTrue("Created path must be a root path", errorPath
+ .isRootPath());
+
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomConstraintValidatorFactoryTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomConstraintValidatorFactoryTest.java
new file mode 100644
index 0000000..a9babfb
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomConstraintValidatorFactoryTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+
+import org.apache.bval.jsr303.CustomConstraintValidatorFactoryTest.GoodPerson.GoodPersonValidator;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks that overriding the default {@link ConstraintValidatorFactory} works
+ * as expected.
+ *
+ * @author Carlos Vara
+ */
+public class CustomConstraintValidatorFactoryTest extends TestCase {
+
+ /**
+ * If the custom ConstraintValidatorFactory returns <code>null</code> for a
+ * valid {@link ConstraintValidatorFactory#getInstance(Class)} call, a
+ * validation exception should be thrown.
+ */
+ public void testValidationExceptionWhenFactoryReturnsNullValidator() {
+
+ ConstraintValidatorFactory customFactory = new ConstraintValidatorFactory() {
+ // @Override - not allowed in 1.5 for Interface methods
+ public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+ return null; // always return null
+ }
+ };
+
+ // Create a validator with this factory
+ ApacheValidatorConfiguration customConfig = Validation.byProvider(ApacheValidationProvider.class).configure().constraintValidatorFactory(customFactory);
+ Validator validator = customConfig.buildValidatorFactory().getValidator();
+
+ try {
+ validator.validate(new Person());
+ fail("ValidationException must be thrown when factory returns a null constraint validator.");
+ } catch (ValidationException e) {
+ // correct
+ }
+ }
+
+ @GoodPerson
+ public static class Person {
+ }
+
+ @Constraint(validatedBy = { GoodPersonValidator.class })
+ @Target({ METHOD, FIELD, ANNOTATION_TYPE, TYPE })
+ @Retention(RUNTIME)
+ @Documented
+ public static @interface GoodPerson {
+
+ String message() default "Not a good person";
+ Class<?>[] groups() default { };
+ Class<? extends Payload>[] payload() default {};
+
+ public static class GoodPersonValidator implements ConstraintValidator<GoodPerson, Person> {
+ // @Override - not allowed in 1.5 for Interface methods
+ public void initialize(GoodPerson constraintAnnotation) {
+ }
+
+ // @Override - not allowed in 1.5 for Interface methods
+ public boolean isValid(Person value, ConstraintValidatorContext context) {
+ return true;
+ }
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomValidatorFactoryTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomValidatorFactoryTest.java
new file mode 100644
index 0000000..d2fc38d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomValidatorFactoryTest.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import junit.framework.TestCase;
+
+import javax.validation.*;
+import javax.validation.spi.ConfigurationState;
+
+/**
+ * Test the ability to force a particular {@link ValidatorFactory}
+ * implementation class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CustomValidatorFactoryTest extends TestCase {
+
+ public static class CustomValidatorFactory extends ApacheValidatorFactory {
+
+ /**
+ * Create a new CustomValidatorFactory instance.
+ *
+ * @param configurationState
+ */
+ public CustomValidatorFactory(ConfigurationState configurationState) {
+ super(configurationState);
+ }
+ }
+
+ public static class IncompatibleValidatorFactory implements ValidatorFactory {
+
+ public ConstraintValidatorFactory getConstraintValidatorFactory() {
+ return null;
+ }
+
+ public MessageInterpolator getMessageInterpolator() {
+ return null;
+ }
+
+ public TraversableResolver getTraversableResolver() {
+ return null;
+ }
+
+ public Validator getValidator() {
+ return null;
+ }
+
+ public <T> T unwrap(Class<T> type) {
+ return null;
+ }
+
+ public ValidatorContext usingContext() {
+ return null;
+ }
+
+ }
+
+ public static class NotAValidatorFactory {
+ public NotAValidatorFactory(ConfigurationState configurationState) {
+ }
+ }
+
+ public void testDefaultValidatorFactory() {
+ Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().unwrap(
+ ApacheValidatorFactory.class);
+ }
+
+ public void testNoSuchType() {
+ try {
+ Validation.byProvider(ApacheValidationProvider.class).configure().addProperty(
+ ApacheValidatorConfiguration.Properties.VALIDATOR_FACTORY_CLASSNAME, "no.such.type")
+ .buildValidatorFactory();
+ fail();
+ } catch (ValidationException ex) {
+ assertTrue(ex.getCause() instanceof ClassNotFoundException);
+ }
+ }
+
+ public void testCustomValidatorFactory() {
+ doTest(CustomValidatorFactory.class, null);
+ }
+
+ public void testInvalidType() {
+ doTest(NotAValidatorFactory.class, ClassCastException.class);
+ }
+
+ public void testUnsupportedValidatorFactoryType() {
+ doTest(IncompatibleValidatorFactory.class, NoSuchMethodException.class);
+ }
+
+ private void doTest(Class<?> validatorFactoryType, Class<? extends Exception> expectedFailureCause) {
+ try {
+ Validation.byProvider(ApacheValidationProvider.class).configure().addProperty(
+ ApacheValidatorConfiguration.Properties.VALIDATOR_FACTORY_CLASSNAME, validatorFactoryType.getName())
+ .buildValidatorFactory().unwrap(validatorFactoryType);
+ assertNull(expectedFailureCause);
+ } catch (ValidationException ex) {
+ assertNotNull(expectedFailureCause);
+ assertTrue(expectedFailureCause.isInstance(ex.getCause()));
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/DefaultMessageInterpolatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/DefaultMessageInterpolatorTest.java
new file mode 100644
index 0000000..369ce09
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/DefaultMessageInterpolatorTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.example.Author;
+import org.apache.bval.jsr303.example.PreferredGuest;
+
+import javax.validation.MessageInterpolator;
+import javax.validation.Validator;
+import javax.validation.constraints.Pattern;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.util.Locale;
+
+/**
+ * MessageResolverImpl Tester.
+ */
+public class DefaultMessageInterpolatorTest extends TestCase {
+
+ private DefaultMessageInterpolator interpolator;
+
+ public DefaultMessageInterpolatorTest(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ return new TestSuite(DefaultMessageInterpolatorTest.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp(); // call super!
+ interpolator = new DefaultMessageInterpolator();
+ interpolator.setLocale(Locale.ENGLISH);
+ }
+
+ public void testCreateResolver() {
+
+ final Validator gvalidator = getValidator();
+
+ assertTrue(!gvalidator.getConstraintsForClass(PreferredGuest.class).getConstraintsForProperty(
+ "guestCreditCardNumber").getConstraintDescriptors().isEmpty());
+
+ MessageInterpolator.Context ctx = new MessageInterpolator.Context() {
+
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return (ConstraintDescriptor<?>) gvalidator.getConstraintsForClass(PreferredGuest.class)
+ .getConstraintsForProperty("guestCreditCardNumber").getConstraintDescriptors().iterator().next();
+ }
+
+ public Object getValidatedValue() {
+ return "12345678";
+ }
+ };
+ String msg = interpolator.interpolate("{validator.creditcard}", ctx);
+ Assert.assertEquals("credit card is not valid", msg);
+
+ ctx = new MessageInterpolator.Context() {
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return gvalidator.getConstraintsForClass(Author.class).getConstraintsForProperty("lastName")
+ .getConstraintDescriptors().iterator().next();
+ }
+
+ public Object getValidatedValue() {
+ return "";
+ }
+ };
+
+ msg = interpolator.interpolate("{org.apache.bval.constraints.NotEmpty.message}", ctx);
+ Assert.assertEquals("may not be empty", msg);
+ }
+
+ /**
+ * Checks that strings containing special characters are correctly
+ * substituted when interpolating.
+ */
+ public void testReplacementWithSpecialChars() {
+
+ final Validator validator = getValidator();
+ MessageInterpolator.Context ctx;
+
+ // Try to interpolate an annotation attribute containing $
+ ctx = new MessageInterpolator.Context() {
+
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return (ConstraintDescriptor<?>) validator.getConstraintsForClass(Person.class)
+ .getConstraintsForProperty("idNumber").getConstraintDescriptors().iterator().next();
+ }
+
+ public Object getValidatedValue() {
+ return "12345678";
+ }
+ };
+
+ String result = this.interpolator.interpolate("Id number should match {regexp}", ctx);
+ Assert.assertEquals("Incorrect message interpolation when $ is in an attribute",
+ "Id number should match ....$", result);
+
+ // Try to interpolate an annotation attribute containing \
+ ctx = new MessageInterpolator.Context() {
+
+ public ConstraintDescriptor<?> getConstraintDescriptor() {
+ return (ConstraintDescriptor<?>) validator.getConstraintsForClass(Person.class)
+ .getConstraintsForProperty("otherId").getConstraintDescriptors().iterator().next();
+ }
+
+ public Object getValidatedValue() {
+ return "12345678";
+ }
+ };
+
+ result = this.interpolator.interpolate("Other id should match {regexp}", ctx);
+ Assert.assertEquals("Incorrect message interpolation when \\ is in an attribute value",
+ "Other id should match .\\n", result);
+
+ }
+
+ public static class Person {
+
+ @Pattern(message = "Id number should match {regexp}", regexp = "....$")
+ public String idNumber;
+
+ @Pattern(message = "Other id should match {regexp}", regexp = ".\\n")
+ public String otherId;
+
+ }
+
+ private Validator getValidator() {
+ return ApacheValidatorFactory.getDefault().getValidator();
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ExceptionsContractTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ExceptionsContractTest.java
new file mode 100644
index 0000000..302fc07
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ExceptionsContractTest.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.metadata.BeanDescriptor;
+import java.util.Locale;
+
+/**
+ * Several checks to validate that the implementations of {@link Validator} and
+ * {@link BeanDescriptor} throw the correct exceptions as per the spec.
+ *
+ * @author Carlos Vara
+ */
+public class ExceptionsContractTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ /**
+ * Checks that the correct exception is thrown when validating a bean whose
+ * getter throws an exception.
+ */
+ public void testExceptionThrowingBean() {
+ try {
+ validator.validate(new ExceptionThrowingBean());
+ Assert.fail("No exception thrown when validating a bean whose getter throws a RTE");
+ } catch (ValidationException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when passing
+ * <code>null</code> as group array.
+ */
+ public void testValidateNullGroup() {
+ try {
+ Class<?>[] groups = null;
+ validator.validate(new String(), groups);
+ Assert.fail("No exception thrown when passing null as group array");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when passing an
+ * invalid property name.
+ */
+ public void testValidateInvalidPropertyName() {
+
+ // Null propertyName
+ try {
+ validator.validateProperty(new Person(), null);
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+
+ // Empty propertyName
+ try {
+ validator.validateProperty(new Person(), "");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+
+ // Invalid propertyName
+ try {
+ validator.validateProperty(new Person(), "surname");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when trying to
+ * validate a property on a null object.
+ */
+ public void testValidatePropertyOnNullBean() {
+ try {
+ validator.validateProperty(null, "class");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when passing
+ * <code>null</code> as group array in a
+ * {@link Validator#validateProperty(Object, String, Class...)} call.
+ */
+ public void testValidatePropertyNullGroup() {
+ try {
+ Class<?>[] groups = null;
+ validator.validateProperty(new Person(), "name", groups);
+ Assert.fail("No exception thrown when passing null as group array");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when calling
+ * {@link Validator#validateValue(Class, String, Object, Class...)} with a
+ * <code>null</code> class.
+ */
+ public void testValidateValueOnNullClass() {
+ try {
+ validator.validateValue(null, "class", Object.class);
+ Assert.fail("No exception thrown when passing null as group array");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when passing an
+ * invalid property name to
+ * {@link Validator#validateValue(Class, String, Object, Class...)}.
+ */
+ public void testValidateValueInvalidPropertyName() {
+ // Null propertyName
+ try {
+ validator.validateValue(Person.class, null, "John");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+
+ // Empty propertyName
+ try {
+ validator.validateValue(Person.class, "", "John");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+
+ // Invalid propertyName
+ try {
+ validator.validateValue(Person.class, "unexistant", "John");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when calling
+ * {@link Validator#validateValue(Class, String, Object, Class...)} with a
+ * <code>null</code> group array.
+ */
+ public void testValidateValueNullGroup() {
+ try {
+ Class<?>[] groups = null;
+ validator.validateValue(Person.class, "name", "John", groups);
+ Assert.fail("No exception thrown when passing null as group array");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Enforces the "not a valid object property" part of the {@link IllegalArgumentException}
+ * declaration on {@link Validator#validateValue(Class, String, Object, Class...)}
+ */
+ public void testValidateIncompatibleValue() {
+ try {
+ validator.validateValue(Person.class, "name", 666);
+ Assert.fail("No exception thrown when passing Integer for string value");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ try {
+ validator.validateValue(Person.class, "age", null);
+ Assert.fail("No exception thrown when passing null for primitive value");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ /**
+ * Checks that an {@link IllegalArgumentException} is thrown when calling
+ * {@link BeanDescriptor#getConstraintsForProperty(String)} with an invalid
+ * property name.
+ */
+ public void testGetConstraintsForInvalidProperty() {
+ BeanDescriptor personDescriptor = validator.getConstraintsForClass(Person.class);
+
+ try {
+ personDescriptor.getConstraintsForProperty(null);
+ fail("No exception thrown when calling getConstraintsForProperty with null property");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+
+ try {
+ personDescriptor.getConstraintsForProperty("");
+ fail("No exception thrown when calling getConstraintsForProperty with empty property");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ }
+ }
+
+ public static class ExceptionThrowingBean {
+
+ @NotNull
+ public String getValue() {
+ throw new IllegalStateException();
+ }
+
+ }
+
+ public static class Person {
+
+ @NotNull
+ public String name;
+
+ @Min(0)
+ public int age;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/FooTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/FooTest.java
new file mode 100644
index 0000000..359552c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/FooTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import junit.framework.TestCase;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validator;
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Description: <br/>
+ */
+public class FooTest extends TestCase {
+
+ @Valid
+ private Collection<Foo> foos = new ArrayList<Foo>();
+
+ public FooTest() {
+ foos.add(new Foo("foo1"));
+ foos.add(null);
+ foos.add(new Foo("foo3"));
+ }
+
+
+ public class Foo {
+ @NotNull
+ public String bar;
+
+ public Foo(String bar) {
+ this.bar = bar;
+ }
+
+ }
+
+ public void testValidation() {
+ FooTest t = new FooTest();
+
+ Validator v = ApacheValidatorFactory.getDefault().getValidator();
+ Set<ConstraintViolation<FooTest>> errors = v.validate(t);
+ System.out.println("got errors:");
+ for (ConstraintViolation<?> error : errors) {
+ System.out.println(error.getPropertyPath());
+ }
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/Jsr303Test.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/Jsr303Test.java
new file mode 100644
index 0000000..9dbd117
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/Jsr303Test.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.SizeValidatorForString;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Book;
+import org.apache.bval.jsr303.example.Engine;
+import org.apache.bval.jsr303.example.IllustratedBook;
+import org.apache.bval.jsr303.example.MaxTestEntity;
+import org.apache.bval.jsr303.example.NoValidatorTestEntity;
+import org.apache.bval.jsr303.example.Second;
+import org.apache.bval.jsr303.example.SizeTestEntity;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class Jsr303Test extends TestCase {
+ /*
+ * static { ApacheValidatorFactory.getDefault().getMetaBeanManager()
+ * .addResourceLoader("org/apache/bval/example/test-beanInfos.xml"); }
+ */
+
+ /*
+ * public void testUseCoreXmlMetaData() { Validator validator =
+ * getValidator();
+ *
+ * BusinessObject object = new BusinessObject();
+ * object.setTitle("1234567834567 too long title ");
+ * Set<ConstraintViolation<BusinessObject>> violations =
+ * validator.validate(object); Assert.assertNotNull(violations);
+ * Assert.assertTrue(!violations.isEmpty());
+ *
+ * Assert.assertTrue(!validator.validateProperty(object,
+ * "title").isEmpty()); }
+ */
+
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ public void testPropertyDescriptorHasConstraints() {
+ BeanDescriptor cons = validator.getConstraintsForClass(Book.class);
+ assertTrue(cons.getConstraintsForProperty("author").hasConstraints());
+ assertTrue(cons.getConstraintsForProperty("title").hasConstraints());
+ assertTrue(cons.getConstraintsForProperty("uselessField").hasConstraints());
+ // cons.getConstraintsForProperty("unconstraintField") == null without
+ // Introspector
+ // cons.getConstraintsForProperty("unconstraintField") != null with
+ // Introspector
+ assertTrue(cons.getConstraintsForProperty("unconstraintField") == null
+ || !cons.getConstraintsForProperty("unconstraintField").hasConstraints());
+ assertNull(cons.getConstraintsForProperty("unknownField"));
+ }
+
+ public void testValidateValue() {
+ assertTrue(validator.validateValue(Book.class, "subtitle", "123456789098765432").isEmpty());
+ assertFalse(validator.validateValue(Book.class, "subtitle",
+ "123456789098765432123412345678909876543212341234564567890987654321234", Second.class).isEmpty());
+ // tests for issue 22: validation of a field without any constraints
+ assertEquals(0, validator.validateValue(Book.class, "unconstraintField", 4).size());
+ // tests for issue 22: validation of unknown field cause
+ // ValidationException
+ try {
+ validator.validateValue(Book.class, "unknownProperty", 4);
+ fail("unknownProperty not detected");
+ } catch (IllegalArgumentException ex) {
+ // OK
+ assertEquals("unknown property 'unknownProperty' in org.apache.bval.jsr303.example.Book", ex.getMessage());
+ }
+ }
+
+ public void testValidateNonCascadedRealNestedProperty() {
+ try {
+ validator.validateValue(IllustratedBook.class, "illustrator.firstName", "Edgar");
+ fail("unknownProperty not detected");
+ } catch (IllegalArgumentException ex) {
+ // OK
+ assertEquals("Property org.apache.bval.jsr303.example.IllustratedBook.illustrator is not cascaded", ex.getMessage());
+ }
+ }
+
+ public void testMetadataAPI_Book() {
+ Assert.assertNotNull(validator.getConstraintsForClass(Book.class));
+ // not necessary for implementation correctness, but we'll test
+ // nevertheless:
+ Assert.assertSame(validator.getConstraintsForClass(Book.class), validator.getConstraintsForClass(Book.class));
+ BeanDescriptor bc = validator.getConstraintsForClass(Book.class);
+ // assertEquals(ElementType.TYPE, bc.getElementType());
+ Assert.assertEquals(Book.class, bc.getElementClass());
+ // assertEquals(false, bc.isCascaded());
+ // assertEquals("", bc.getPropertyPath());
+ Assert.assertTrue(bc.getConstraintDescriptors() != null);
+ TestUtils.failOnModifiable(bc.getConstraintDescriptors(), "beanDescriptor constraintDescriptors");
+ }
+
+ public void testMetadataAPI_Engine() {
+ ElementDescriptor desc =
+ validator.getConstraintsForClass(Engine.class).getConstraintsForProperty("serialNumber");
+ assertNotNull(desc);
+ // assertEquals(ElementType.FIELD, desc.getElementType());
+ Assert.assertEquals(String.class, desc.getElementClass());
+ }
+
+ public void testMetadataAPI_Address() {
+ Assert.assertFalse(validator.getConstraintsForClass(Address.class).getConstraintDescriptors().isEmpty());
+
+ Set<PropertyDescriptor> props = validator.getConstraintsForClass(Address.class).getConstrainedProperties();
+ TestUtils.failOnModifiable(props, "beanDescriptor constrainedProperties");
+ Set<String> propNames = new HashSet<String>(props.size());
+ for (PropertyDescriptor each : props) {
+ TestUtils.failOnModifiable(each.getConstraintDescriptors(), "propertyDescriptor constraintDescriptors");
+ propNames.add(each.getPropertyName());
+ }
+ Assert.assertTrue(propNames.contains("addressline1")); // annotated at
+ // field level
+ Assert.assertTrue(propNames.contains("addressline2"));
+ Assert.assertTrue(propNames.contains("zipCode"));
+ Assert.assertTrue(propNames.contains("country"));
+ Assert.assertTrue(propNames.contains("city")); // annotated at method
+ // level
+ Assert.assertEquals(5, props.size());
+
+ ElementDescriptor desc =
+ validator.getConstraintsForClass(Address.class).getConstraintsForProperty("addressline1");
+ Assert.assertNotNull(desc);
+ boolean found = false;
+ for (ConstraintDescriptor<?> each : desc.getConstraintDescriptors()) {
+ if (each.getConstraintValidatorClasses().get(0).equals(SizeValidatorForString.class)) {
+ Assert.assertTrue(each.getAttributes().containsKey("max"));
+ assertEquals(30, each.getAttributes().get("max"));
+ found = true;
+ }
+ }
+ Assert.assertTrue(found);
+
+ }
+
+ public void testValidateMultiValuedConstraints() {
+ Engine engine = new Engine();
+ engine.serialNumber = "abcd-defg-0123";
+ Set<ConstraintViolation<Engine>> violations;
+ violations = validator.validate(engine);
+ assertEquals(0, violations.size());
+
+ engine.serialNumber = "!)/(/()";
+ violations = validator.validate(engine);
+ assertEquals(2, violations.size());
+ for (String msg : new String[] { "must contain alphabetical characters only", "must match ....-....-...." }) {
+ assertNotNull(TestUtils.getViolationWithMessage(violations, msg));
+ }
+ }
+
+ public void testConstraintValidatorResolutionAlgorithm() {
+ MaxTestEntity entity = new MaxTestEntity();
+ entity.setText("101");
+ entity.setProperty("201");
+ entity.setLongValue(301);
+ entity.setDecimalValue(new BigDecimal(401));
+ Set<ConstraintViolation<MaxTestEntity>> violations = validator.validate(entity);
+ assertEquals(4, violations.size());
+
+ NoValidatorTestEntity entity2 = new NoValidatorTestEntity();
+ try {
+ validator.validate(entity2);
+ fail("UnexpectedTypeException expected but not thrown");
+ } catch (UnexpectedTypeException ex) {
+ // we expected this
+ assertEquals("No validator could be found for type java.lang.Object. "
+ + "See: @Max at private java.lang.Object " + "org.apache.bval.jsr303.example."
+ + "NoValidatorTestEntity.anything", ex.getMessage());
+ }
+ }
+
+ public void testSizeValidation() {
+ SizeTestEntity en = new SizeTestEntity();
+ en.ba = new byte[3];
+ en.ca = new char[3];
+ en.boa = new boolean[3];
+ en.coll = Arrays.asList("1", "2", "3");
+ en.da = new double[3];
+ en.fa = new float[3];
+ en.it = new int[3];
+ en.la = new long[3];
+ en.map = new HashMap<String, String>();
+ en.map.put("1", "1");
+ en.map.put("3", "3");
+ en.map.put("2", "2");
+ en.oa = new Integer[3];
+ en.oa2 = new Integer[3];
+ en.sa = new short[3];
+ en.text = "123";
+ Set<ConstraintViolation<SizeTestEntity>> vi = validator.validate(en);
+ assertEquals(13, vi.size());
+ }
+
+ /**
+ * JSR-303 Section 5.1.c, IllegalArgumentException should be thrown
+ */
+ public void testGetConstraintsForNullClass() {
+ try {
+ validator.getConstraintsForClass(null);
+ Assert.fail("No exception thrown on Validator.getConstraintsForClass(null)");
+ } catch (IllegalArgumentException e) {
+ // Correct
+ return;
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/PayloadTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/PayloadTest.java
new file mode 100644
index 0000000..54fa661
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/PayloadTest.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+
+import junit.framework.TestCase;
+
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: test that payload information can be retrieved
+ * from error reports via the ConstraintDescriptor either accessed
+ * through the ConstraintViolation objects<br/>
+ */
+public class PayloadTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ static class Severity {
+ static class Info implements Payload {
+ }
+
+ static class Error implements Payload {
+ }
+ }
+
+ static class Address {
+ private String zipCode;
+ private String city;
+
+ Address(String zipCode, String city) {
+ this.zipCode = zipCode;
+ this.city = city;
+ }
+
+ @NotNull(message = "would be nice if we had one", payload = Severity.Info.class)
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ @NotNull(message = "the city is mandatory", payload = Severity.Error.class)
+ public String getCity() {
+ return city;
+ }
+ }
+
+ public void testPayload() {
+ Set<ConstraintViolation<Address>> violations;
+ Address address = new Address(null, null);
+ violations = validator.validate(address);
+ assertEquals(2, violations.size());
+ ConstraintViolation<?> vio;
+ vio = TestUtils.getViolation(violations, "zipCode");
+ assertNotNull(vio);
+ assertEquals(1, vio.getConstraintDescriptor().getPayload().size());
+ assertTrue(
+ vio.getConstraintDescriptor().getPayload().contains(Severity.Info.class));
+
+ vio = TestUtils.getViolation(violations, "city");
+ assertNotNull(vio);
+ assertEquals(1, vio.getConstraintDescriptor().getPayload().size());
+ assertTrue(
+ vio.getConstraintDescriptor().getPayload().contains(Severity.Error.class));
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/TckReproducerTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/TckReproducerTest.java
new file mode 100644
index 0000000..92d9806
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/TckReproducerTest.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303;
+
+import junit.framework.TestCase;
+import org.apache.bval.util.PropertyAccess;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Pattern;
+
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 21.04.2010<br>
+ * Time: 14:21:45<br>
+ */
+public class TckReproducerTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ public static <T> void assertCorrectNumberOfViolations(Set<ConstraintViolation<T>> violations,
+ int expectedViolations) {
+ assertEquals("Wrong number of constraint violations. Expected: " + expectedViolations + " Actual: "
+ + violations.size(), expectedViolations, violations.size());
+ }
+
+ public void testPropertyAccessOnNonPublicClass() throws Exception {
+ Car car = new Car("USd-298");
+ assertEquals(car.getLicensePlateNumber(), PropertyAccess.getProperty(car, "licensePlateNumber"));
+
+ Set<ConstraintViolation<Car>> violations =
+ validator.validateProperty(car, "licensePlateNumber", First.class,
+ org.apache.bval.jsr303.example.Second.class);
+ assertCorrectNumberOfViolations(violations, 1);
+
+ car.setLicensePlateNumber("USD-298");
+ violations =
+ validator.validateProperty(car, "licensePlateNumber", First.class,
+ org.apache.bval.jsr303.example.Second.class);
+ assertCorrectNumberOfViolations(violations, 0);
+ }
+
+ class Car {
+ @Pattern(regexp = "[A-Z][A-Z][A-Z]-[0-9][0-9][0-9]", groups = { First.class, Second.class })
+ private String licensePlateNumber;
+
+ Car(String licensePlateNumber) {
+ this.licensePlateNumber = licensePlateNumber;
+ }
+
+ public String getLicensePlateNumber() {
+ return licensePlateNumber;
+ }
+
+ public void setLicensePlateNumber(String licensePlateNumber) {
+ this.licensePlateNumber = licensePlateNumber;
+ }
+ }
+
+ interface First {
+ }
+
+ interface Second {
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java
new file mode 100644
index 0000000..8f571fb
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java
@@ -0,0 +1,722 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import javax.validation.groups.Default;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.NotNullValidator;
+import org.apache.bval.jsr303.example.AccessTestBusinessObject;
+import org.apache.bval.jsr303.example.AccessTestBusinessObjectSub;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Author;
+import org.apache.bval.jsr303.example.Book;
+import org.apache.bval.jsr303.example.BusinessAddress;
+import org.apache.bval.jsr303.example.Continent;
+import org.apache.bval.jsr303.example.Country;
+import org.apache.bval.jsr303.example.First;
+import org.apache.bval.jsr303.example.Last;
+import org.apache.bval.jsr303.example.RecursiveFoo;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class ValidationTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ public void testAccessStrategies_field_method() {
+ AccessTestBusinessObject o1 = new AccessTestBusinessObject("1");
+ AccessTestBusinessObjectSub o2 = new AccessTestBusinessObjectSub("3");
+ Set<ConstraintViolation<AccessTestBusinessObject>> errors = validator.validate(o1);
+ assertTrue(errors.isEmpty());
+ Set<ConstraintViolation<AccessTestBusinessObjectSub>> errors2 = validator.validate(o2);
+ assertTrue(errors2.isEmpty());
+
+ o2 = new AccessTestBusinessObjectSub("1");
+ errors2 = validator.validate(o2);
+ assertEquals(1, errors2.size());
+
+ // assert, that getvar2() and getVar2() are both validated with their
+ // getter method
+ o2 = new AccessTestBusinessObjectSub("3");
+ o2.setVar2("1");
+ o2.setvar2("2");
+ errors2 = validator.validate(o2);
+ assertEquals(2, errors2.size());
+
+ o2.setvar2("5");
+ o2.setVar2("6");
+ errors2 = validator.validate(o2);
+ assertEquals(0, errors2.size());
+
+ o2.setvar2("5");
+ o2.setVar2("-1");
+ errors2 = validator.validate(o2);
+ assertEquals(1, errors2.size());
+ }
+
+ public void testAccessStrategies_on_children() {
+ AccessTestBusinessObject o1 = new AccessTestBusinessObject("1");
+ AccessTestBusinessObject o2 = new AccessTestBusinessObject("2");
+ o1.next(o2);
+ Set<ConstraintViolation<AccessTestBusinessObject>> errors = validator.validate(o1);
+ // assert, that field access 'next' is used and not getNext() is
+ // called!!!
+ assertEquals(1, errors.size());
+ o2 = new AccessTestBusinessObject("1");
+ o1.next(o2);
+ errors = validator.validate(o1);
+ assertEquals(0, errors.size());
+
+ // assert that toBeIgnored not validated, because not annotated with
+ // @Valid
+ o1.setToBeIgnored(new AccessTestBusinessObject("99"));
+ errors = validator.validate(o1);
+ assertEquals(0, errors.size());
+
+ o1.setNext(new AccessTestBusinessObject("99"));
+ errors = validator.validate(o1);
+ assertEquals(1, errors.size());
+ }
+
+ public void testBook() {
+ Author author = new Author();
+ author.setLastName("Baudelaire");
+ author.setFirstName("");
+ Book book = new Book();
+ book.setAuthor(author);
+ book.setSubtitle("12345678900125678901234578901234567890");
+
+ // NotEmpty failure on the title field
+ Set<ConstraintViolation<Book>> errors = validator.validate(book, Book.All.class);
+ Assert.assertTrue(!errors.isEmpty());
+
+ book.setTitle("Les fleurs du mal");
+ author.setCompany("Some random publisher with a very very very long name");
+
+ // author.firstName fails to pass the NotEmpty constraint
+ // author.company fails to pass the Size constraint
+ }
+
+ /**
+ * test: - dynamic resolution of associated object types. - inheritance of validation constraints - complex
+ * valiation, different groups, nested object net
+ */
+ public void testValidAnnotation() {
+ Author a = new Author();
+ a.setAddresses(new ArrayList<Address>());
+ BusinessAddress adr = new BusinessAddress();
+ adr.setCountry(new Country());
+ adr.setAddressline1("line1");
+ adr.setAddressline2("line2");
+
+ adr.setZipCode("1234567890123456789");
+ a.getAddresses().add(adr);
+
+ a.setFirstName("Karl");
+ a.setLastName("May");
+
+ Set<ConstraintViolation<Author>> found = validator.validate(a, Default.class, First.class, Last.class);
+ Assert.assertTrue(!found.isEmpty());
+ Assert.assertEquals(4, found.size());
+
+ adr.setCity("Berlin");
+ adr.setZipCode("12345");
+ adr.setCompany("apache");
+ found = validator.validate(a, Default.class, First.class, Last.class);
+ Assert.assertEquals(1, found.size());
+ ConstraintViolation<Author> ic = found.iterator().next();
+ Assert.assertEquals("addresses[0].country.name", ic.getPropertyPath().toString());
+ }
+
+ public void testPropertyPathWithIndex() {
+ Author a = new Author();
+ a.setAddresses(new ArrayList<Address>());
+ Address adr = new Address();
+ adr.setAddressline1("adr1");
+ adr.setCity("Santiago");
+ a.getAddresses().add(adr);
+ adr = new Address();
+ adr.setAddressline1("adr2");
+ adr.setCity("Havanna");
+ a.getAddresses().add(adr);
+ adr = new Address();
+ adr.setAddressline1("adr3");
+ adr.setCity("Trinidad");
+ a.getAddresses().add(adr);
+
+ Set<ConstraintViolation<Author>> constraints = validator.validate(a);
+ Assert.assertTrue(!constraints.isEmpty());
+
+ assertPropertyPath("addresses[0].country", constraints);
+ assertPropertyPath("addresses[1].country", constraints);
+ assertPropertyPath("addresses[2].country", constraints);
+ }
+
+ /**
+ * Check correct path reporting when validating a set of beans.
+ */
+ public void testPropertyPathOnSet() {
+ Continent c = new Continent();
+ c.name = "c1";
+ Country country = new Country();
+ country.setISO2Code("xx");
+ country.setISO3Code("xxx");
+ country.setName(null);
+ c.countries.add(country);
+
+ Set<ConstraintViolation<Continent>> constraints = validator.validate(c);
+ Assert.assertEquals("Incorrect number of violations detected", 1, constraints.size());
+ assertPropertyPath("countries[].name", constraints);
+
+ }
+
+ private <T> void assertPropertyPath(String propertyPath, Set<ConstraintViolation<T>> constraints) {
+ for (ConstraintViolation<T> each : constraints) {
+ if (each.getPropertyPath().toString().equals(propertyPath))
+ return;
+ }
+ Assert.fail(propertyPath + " not found in " + constraints);
+ }
+
+ public void testPropertyPathRecursive() {
+ RecursiveFoo foo1 = new RecursiveFoo(); // root
+ RecursiveFoo foo11 = new RecursiveFoo();
+ foo1.getFoos().add(foo11); // foos[0]
+ RecursiveFoo foo12 = new RecursiveFoo();
+ foo1.getFoos().add(foo12); // foos[1]
+ RecursiveFoo foo2 = new RecursiveFoo();
+ foo11.getFoos().add(foo2); // foos[0].foos[0]
+
+ Set<ConstraintViolation<RecursiveFoo>> constraints = validator.validate(foo1);
+ assertPropertyPath("foos[0].foos[0].foos", constraints);
+ assertPropertyPath("foos[1].foos", constraints);
+ }
+
+ public void testNullElementInCollection() {
+ try {
+ validator.validate(null);
+ Assert.fail();
+ } catch (IllegalArgumentException ex) {
+ }
+ RecursiveFoo foo = new RecursiveFoo();
+ foo.getFoos().add(new RecursiveFoo());
+ foo.getFoos().add(null);
+ Assert.assertTrue(!validator.validate(foo).isEmpty());
+ // check that no nullpointer exception gets thrown
+ }
+
+ public void testGroups() {
+ Author author = new Author();
+ author.setCompany("ACME");
+ Book book = new Book();
+ book.setTitle("");
+ book.setAuthor(author);
+ boolean foundTitleConstraint = false;
+ Set<ConstraintViolation<Book>> constraintViolations = validator.validate(book, Book.All.class);
+ assertEquals(1, constraintViolations.size());
+ // assuming an english locale, the interpolated message is returned
+ for (ConstraintViolation<Book> constraintViolation : constraintViolations) {
+ if (constraintViolation.getRootBean().getClass() == Book.class) {
+ Assert.assertEquals("may not be empty", constraintViolation.getMessage());
+ Assert.assertTrue(book == constraintViolation.getRootBean());
+
+ // the offending property
+ if (constraintViolation.getPropertyPath().toString().equals("title")) {
+ foundTitleConstraint = true;
+ // the offending value
+ Assert.assertEquals(book.getTitle(), constraintViolation.getInvalidValue());
+ }
+ }
+ }
+ Assert.assertTrue(foundTitleConstraint);
+ }
+
+ /**
+ * Example 2.14. Using the fluent API to build custom constraint violations. test that: the
+ * {@link org.apache.bval.constraints.ZipCodeCityCoherenceValidator} adds custom messages to the context and
+ * suppresses the default message
+ */
+ public void testConstraintValidatorContextFluentAPI() {
+ Address ad = new Address();
+ ad.setCity("error");
+ ad.setZipCode("error");
+ ad.setAddressline1("something");
+ ad.setCountry(new Country());
+ ad.getCountry().setName("something");
+ Set<ConstraintViolation<Address>> violations = validator.validate(ad);
+ Assert.assertEquals(2, violations.size());
+ for (ConstraintViolation<Address> each : violations) {
+ Assert.assertTrue(each.getMessage().endsWith(" not OK"));
+ }
+ assertNotNull(TestUtils.getViolation(violations, "city"));
+ assertNotNull(TestUtils.getViolation(violations, ""));
+ }
+
+ public void testValidateNestedPropertyPath() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "addresses[0].country.ISO2Code";
+
+ Author author = new Author();
+ author.setAddresses(new ArrayList<Address>());
+ Address adr = new Address();
+ author.getAddresses().add(adr);
+ Country country = new Country();
+ adr.setCountry(country);
+ country.setISO2Code("too_long");
+
+ Set<ConstraintViolation<Author>> iv = validator.validateProperty(author, propPath);
+ Assert.assertEquals(1, iv.size());
+ ConstraintViolation<Author> vio = iv.iterator().next();
+ assertEquals(propPath, vio.getPropertyPath().toString());
+ assertSame(author, vio.getRootBean());
+ assertSame(author.getAddresses().get(0).getCountry(), vio.getLeafBean());
+
+ country.setISO2Code("23");
+ iv = validator.validateProperty(author, propPath);
+ Assert.assertEquals(0, iv.size());
+
+ iv = validator.validateValue(Author.class, propPath, "345");
+ Assert.assertEquals(1, iv.size());
+ vio = iv.iterator().next();
+ assertEquals(propPath, vio.getPropertyPath().toString());
+ assertNull(vio.getRootBean());
+ assertNull(vio.getLeafBean());
+
+ iv = validator.validateValue(Author.class, propPath, "34");
+ Assert.assertEquals(0, iv.size());
+ }
+
+ public void testValidateCascadingNestedBean() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "addresses[0]";
+
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ Author author = new Author();
+ author.setAddresses(new ArrayList<Address>());
+ Address adr = new Address();
+ author.getAddresses().add(adr);
+ Country country = new Country();
+ adr.setCity("dark");
+ adr.setCountry(country);
+
+ Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+ Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+ country.setISO2Code("too_long");
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(3, iv.size()); // null address line 1 + null
+ // country.name + too long
+ // country.iso2code
+
+ country.setISO2Code("23");
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(2, iv.size()); // null address line 1 + null
+ // country.name, country.iso2code
+ // fixed
+
+ Address value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(1, iv.size()); // null country
+
+ value.setCountry(new Country());
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(1, iv.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(0, iv.size());
+ }
+
+ public void testValidateCascadingNestedProperty() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "addresses[0].country";
+
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ Author author = new Author();
+ author.setAddresses(new ArrayList<Address>());
+ Address adr = new Address();
+ author.getAddresses().add(adr);
+ Country country = new Country();
+ adr.setCity("dark");
+ adr.setCountry(country);
+
+ Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+ Assert.assertEquals(0, iv.size());
+
+ country.setISO2Code("too_long");
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(2, iv.size());
+ // country.name + too long
+ // country.iso2code
+
+ country.setISO2Code("23");
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(1, iv.size());
+ // country.name, country.iso2code
+
+ Country value = null;
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(1, iv.size()); // null country
+
+ value = new Country();
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(1, iv.size()); // null country.name
+
+ value.setName("NWO");
+ iv = v.validateValue(Author.class, propPath, value, true);
+ Assert.assertEquals(0, iv.size());
+ }
+
+ public void testValidateCascadingNestedTipProperty() {
+ final String propPath = "addresses[0].country.name";
+
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ Author author = new Author();
+ author.setAddresses(new ArrayList<Address>());
+ Address adr = new Address();
+ author.getAddresses().add(adr);
+ Country country = new Country();
+ adr.setCity("dark");
+ adr.setCountry(country);
+
+ Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+ Assert.assertEquals(1, iv.size());
+
+ iv = v.validateProperty(author, propPath, true);
+ Assert.assertEquals(1, iv.size());
+ }
+
+ public void testValidateCascadingKeyedElement() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "[foo]";
+
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ final Address adr = new Address();
+ @SuppressWarnings("serial")
+ Object map = new HashMap<String, Address>() {
+ {
+ put("foo", adr);
+ }
+ };
+ Country country = new Country();
+ adr.setCity("dark");
+ adr.setCountry(country);
+ Set<ConstraintViolation<Object>> iv = v.validateProperty(map, propPath);
+ Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+ country.setISO2Code("too_long");
+ iv = v.validateProperty(map, propPath, true);
+ Assert.assertEquals(3, iv.size()); // null address line 1 + null
+ // country.name + too long
+ // country.iso2code
+
+ country.setISO2Code("23");
+ iv = v.validateProperty(map, propPath, true);
+ Assert.assertEquals(2, iv.size()); // null address line 1 + null
+ // country.name, country.iso2code
+ // fixed
+
+ Address value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+
+ Set<?> iv2 = v.validateValue(map.getClass(), propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country
+
+ value.setCountry(new Country());
+ iv2 = v.validateValue(map.getClass(), propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv2 = v.validateValue(map.getClass(), propPath, value, true);
+ Assert.assertEquals(0, iv2.size());
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testValidateCascadingKeyedGenericElement() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "[foo]";
+
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ final Address adr = new Address();
+ Object map = new HashMap<String, Address>();
+ ((Map<String, Address>) map).put("foo", adr);
+ Country country = new Country();
+ adr.setCity("dark");
+ adr.setCountry(country);
+ Set<?> iv = v.validateProperty(map, propPath);
+ Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+ country.setISO2Code("too_long");
+ iv = v.validateProperty(map, propPath, true);
+ Assert.assertEquals(3, iv.size()); // null address line 1 + null
+ // country.name + too long
+ // country.iso2code
+
+ country.setISO2Code("23");
+ iv = v.validateProperty(map, propPath, true);
+ Assert.assertEquals(2, iv.size()); // null address line 1 + null
+ // country.name, country.iso2code
+ // fixed
+
+ Address value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+
+ Set<?> iv2 = v.validateValue(Map.class, propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country
+
+ value.setCountry(new Country());
+ iv2 = v.validateValue(Map.class, propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv2 = v.validateValue(Map.class, propPath, value, true);
+ Assert.assertEquals(0, iv2.size());
+ }
+
+ public void testValidateCascadingIndexedElement() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "[0]";
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ Address value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+ Set<ConstraintViolation<Address[]>> iv;
+ Address[] array = { value };
+ iv = v.validateProperty(array, propPath, true);
+ Assert.assertEquals(1, iv.size()); // null country
+
+ value.setCountry(new Country());
+ iv = v.validateProperty(array, propPath, true);
+ Assert.assertEquals(1, iv.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv = v.validateProperty(array, propPath, true);
+ Assert.assertEquals(0, iv.size());
+
+ value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+ Set<?> iv2;
+ iv2 = v.validateValue(array.getClass(), propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country
+
+ value.setCountry(new Country());
+ iv2 = v.validateValue(array.getClass(), propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv2 = v.validateValue(array.getClass(), propPath, value, true);
+ Assert.assertEquals(0, iv2.size());
+ }
+
+ public void testValidateCascadingIndexedGenericElement() throws InvocationTargetException, NoSuchMethodException,
+ IllegalAccessException {
+ final String propPath = "[0]";
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ Address value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+ Set<?> iv;
+ Object list = Collections.singletonList(value);
+ iv = v.validateProperty(list, propPath, true);
+ Assert.assertEquals(1, iv.size()); // null country
+
+ value.setCountry(new Country());
+ iv = v.validateProperty(list, propPath, true);
+ Assert.assertEquals(1, iv.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv = v.validateProperty(list, propPath, true);
+ Assert.assertEquals(0, iv.size());
+
+ value = new Address();
+ value.setCity("whatever");
+ value.setAddressline1("1 address line");
+ Set<?> iv2;
+ iv2 = v.validateValue(List.class, propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country
+
+ value.setCountry(new Country());
+ iv2 = v.validateValue(List.class, propPath, value, true);
+ Assert.assertEquals(1, iv2.size()); // null country.name
+
+ value.getCountry().setName("NWO");
+ iv2 = v.validateValue(List.class, propPath, value, true);
+ Assert.assertEquals(0, iv2.size());
+ }
+
+ public interface Foo {
+ }
+
+ public static class FooAddress extends Address {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @NotNull(groups = Foo.class)
+ public String getCity() {
+ return super.getCity();
+ }
+ }
+
+ public void testValidateCascadingPropertyWithMultipleGroupsIgnoresSiblingProperties() {
+ final String propPath = "addresses[0].country";
+
+ CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+ Author author = new Author();
+ author.setAddresses(new ArrayList<Address>());
+ Address adr = new FooAddress();
+ author.getAddresses().add(adr);
+ Country country = new Country();
+ adr.setCountry(country);
+
+ Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath, true, Default.class, Foo.class);
+ Assert.assertEquals(1, iv.size());
+ }
+
+ public void testMetadataAPI() {
+ BeanDescriptor bookBeanDescriptor = validator.getConstraintsForClass(Book.class);
+
+ // expect no constraints on Book's Class-Level
+ Assert.assertFalse(bookBeanDescriptor.hasConstraints());
+ // but there are constraints on Book's Property-Level
+ Assert.assertTrue(bookBeanDescriptor.isBeanConstrained());
+ Assert.assertTrue(bookBeanDescriptor.getConstraintDescriptors().size() == 0); // no
+ // constraint
+ // more specifically "author" and "title"
+ Assert.assertEquals(4, bookBeanDescriptor.getConstrainedProperties().size());
+ // not a property
+ Assert.assertTrue(bookBeanDescriptor.getConstraintsForProperty("doesNotExist") == null);
+ // property with no constraint
+ Assert.assertTrue(bookBeanDescriptor.getConstraintsForProperty("description") == null);
+ PropertyDescriptor propertyDescriptor = bookBeanDescriptor.getConstraintsForProperty("title");
+ Assert.assertEquals(2, propertyDescriptor.getConstraintDescriptors().size());
+ Assert.assertTrue("title".equals(propertyDescriptor.getPropertyName()));
+ // assuming the implementation returns the NotEmpty constraint first
+ Iterator<ConstraintDescriptor<?>> iter = propertyDescriptor.getConstraintDescriptors().iterator();
+ ConstraintDescriptor<?> constraintDescriptor = null;
+ while (iter.hasNext()) {
+ constraintDescriptor = iter.next();
+ if (constraintDescriptor.getAnnotation().annotationType().equals(NotNull.class)) {
+ break;
+ }
+
+ }
+ Assert.assertTrue(constraintDescriptor != null);
+ Assert.assertTrue(constraintDescriptor.getGroups().size() == 1); // "first"
+ Assert.assertEquals(NotNullValidator.class, constraintDescriptor.getConstraintValidatorClasses().get(0));
+ // assuming the implementation returns the Size constraint first
+ propertyDescriptor = bookBeanDescriptor.getConstraintsForProperty("subtitle");
+ Iterator<ConstraintDescriptor<?>> iterator = propertyDescriptor.getConstraintDescriptors().iterator();
+ constraintDescriptor = iterator.next();
+ Assert.assertTrue(constraintDescriptor.getAnnotation().annotationType().equals(Size.class));
+ Assert.assertTrue(((Integer) constraintDescriptor.getAttributes().get("max")) == 30);
+ Assert.assertTrue(constraintDescriptor.getGroups().size() == 1);
+ propertyDescriptor = bookBeanDescriptor.getConstraintsForProperty("author");
+ Assert.assertTrue(propertyDescriptor.getConstraintDescriptors().size() == 1);
+ Assert.assertTrue(propertyDescriptor.isCascaded());
+ Assert.assertNull(bookBeanDescriptor.getConstraintsForProperty("unconstraintField"));
+ }
+
+ public void testKeyedMetadata() {
+ @SuppressWarnings("serial")
+ BeanDescriptor beanDescriptor = validator.getConstraintsForClass(new HashMap<String, Object>() {
+ }.getClass());
+ Assert.assertNotNull(beanDescriptor);
+ Assert.assertFalse(beanDescriptor.isBeanConstrained());
+ Assert.assertNull(beanDescriptor.getConstraintsForProperty("[foo]"));
+ }
+
+ public void testGenericKeyedMetadata() {
+ BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Map.class);
+ Assert.assertNotNull(beanDescriptor);
+ Assert.assertFalse(beanDescriptor.isBeanConstrained());
+ Assert.assertNull(beanDescriptor.getConstraintsForProperty("[foo]"));
+ }
+
+ public void testIndexedMetadata() {
+ BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Array.newInstance(Author.class, 0).getClass());
+ Assert.assertNotNull(beanDescriptor);
+ Assert.assertFalse(beanDescriptor.isBeanConstrained());
+ Assert.assertNull(beanDescriptor.getConstraintsForProperty("[0]"));
+ }
+
+ public void testGenericIndexedMetadata() {
+ BeanDescriptor beanDescriptor = validator.getConstraintsForClass(List.class);
+ Assert.assertNotNull(beanDescriptor);
+ Assert.assertFalse(beanDescriptor.isBeanConstrained());
+ Assert.assertNull(beanDescriptor.getConstraintsForProperty("[0]"));
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidatorResolutionTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidatorResolutionTest.java
new file mode 100644
index 0000000..2417582
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidatorResolutionTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303;
+
+import junit.framework.TestCase;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+
+/**
+ * Checks the correct behavior of the validator resolution algorithm.
+ *
+ * @author Carlos Vara
+ */
+public class ValidatorResolutionTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ /**
+ * Check that a {@link ConstraintDefinitionException} is thrown when the
+ * only available validator is associated with a different annotation type.
+ */
+ public void testInvalidValidator() {
+ try {
+ validator.validate(new Person());
+ fail("No exception thrown, but no valid validator available.");
+ } catch (ConstraintDefinitionException e) {
+ // correct
+ }
+ }
+
+ public static class Person {
+ @PersonName
+ public String name;
+ }
+
+ @Constraint(validatedBy = { InvalidPersonNameValidator.class })
+ @Documented
+ @Target( { METHOD, FIELD, TYPE })
+ @Retention(RUNTIME)
+ public static @interface PersonName {
+ String message() default "Wrong person name";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+ }
+
+ public static class InvalidPersonNameValidator implements ConstraintValidator<NotNull, String> {
+ public void initialize(NotNull constraintAnnotation) {
+ // Nothing
+ }
+
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ return true;
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObject.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObject.java
new file mode 100644
index 0000000..6066227
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObject.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.HasValue;
+
+import javax.validation.Valid;
+
+/**
+ * Description: <br/>
+ */
+public class AccessTestBusinessObject {
+ // test that field-access is used, not method-access
+ @HasValue({"1", "3"})
+ protected String var1;
+
+ // test that field-access is used, not method-access
+ @SuppressWarnings("unused")
+ @Valid
+ private AccessTestBusinessObject next;
+
+ // not annotated with @Valid, not validated!!
+ private AccessTestBusinessObject toBeIgnored;
+ private AccessTestBusinessObject _next;
+
+ public AccessTestBusinessObject(String var1) {
+ this.var1 = var1;
+ }
+
+ @HasValue("3")
+ public String getVar1() {
+ return "3";
+ }
+
+ public void next(AccessTestBusinessObject next) {
+ this._next = next;
+ }
+
+
+ public void setNext(AccessTestBusinessObject next) {
+ this.next = next;
+ }
+
+ @Valid
+ public AccessTestBusinessObject getNext() {
+ return _next; // method returns '_next', not the field 'next'
+ }
+
+ public AccessTestBusinessObject getToBeIgnored() {
+ return toBeIgnored;
+ }
+
+ public void setToBeIgnored(AccessTestBusinessObject toBeIgnored) {
+ this.toBeIgnored = toBeIgnored;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObjectSub.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObjectSub.java
new file mode 100644
index 0000000..3aa3b5e
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObjectSub.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import org.apache.bval.constraints.HasValue;
+
+/**
+ * Description: <br/>
+ */
+public class AccessTestBusinessObjectSub extends AccessTestBusinessObject {
+ private String var2, _var2;
+
+ public void setVar2(String var2) {
+ this.var2 = var2;
+ }
+
+ public void setvar2(String _var2) {
+ this._var2 = _var2;
+ }
+
+ public AccessTestBusinessObjectSub(String var1) {
+ super(var1);
+ }
+
+ // getVar1() is called on subclass, although annotated on superclass
+ public String getVar1() {
+ return var1;
+ }
+
+ //// test that getvar2() is called, not getVar2()
+
+ @HasValue("5")
+ public String getvar2() {
+ return _var2;
+ }
+
+ @HasValue("6")
+ public String getVar2() {
+ return var2;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Address.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Address.java
new file mode 100644
index 0000000..d72f2fc
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Address.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.ZipCodeCityCoherence;
+
+import javax.validation.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import javax.validation.groups.Default;
+
+@ZipCodeCityCoherence
+public class Address implements ZipCodeCityCarrier {
+ @NotNull
+ @Size(max = 30)
+ private String addressline1;
+ @Size(max = 30)
+ private String addressline2;
+ @Size(max = 11)
+ private String zipCode;
+ @NotNull
+ @Valid
+ private Country country;
+ private String city;
+
+ public String getAddressline1() {
+ return addressline1;
+ }
+
+ public void setAddressline1(String addressline1) {
+ this.addressline1 = addressline1;
+ }
+
+ public String getAddressline2() {
+ return addressline2;
+ }
+
+ public void setAddressline2(String addressline2) {
+ this.addressline2 = addressline2;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+ @Size(max = 30)
+ @NotNull
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public Country getCountry() {
+ return country;
+ }
+
+ public void setCountry(Country country) {
+ this.country = country;
+ }
+
+ /**
+ * Check conherence on the overall object
+ * Needs basic checking to be green first
+ */
+ public interface HighLevelCoherence {
+ }
+
+ /**
+ * Check both basic constraints and high level ones.
+ * High level constraints are not checked if basic constraints fail.
+ */
+ @GroupSequence(value = {Default.class, HighLevelCoherence.class})
+ public interface Complete {
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Author.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Author.java
new file mode 100644
index 0000000..68e2ca8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Author.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+@GroupSequence({First.class, Author.class, Last.class})
+public class Author {
+ @NotEmpty(groups = Last.class)
+ private String firstName;
+ @NotEmpty(groups = First.class)
+ private String lastName;
+ @Size(max = 40, groups = First.class)
+ private String company;
+
+ @Valid
+ private List<Address> addresses;
+
+ public List<Address> getAddresses() {
+ return addresses;
+ }
+
+ public void setAddresses(List<Address> addresses) {
+ this.addresses = addresses;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Book.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Book.java
new file mode 100644
index 0000000..0d5c7ac
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Book.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+@GroupSequence({First.class, Second.class, Book.class, Last.class})
+public class Book {
+ @NotNull(groups = First.class)
+ @NotEmpty(groups = First.class)
+ private String title;
+
+ @Size(max = 30, groups = Second.class)
+ private String subtitle;
+
+ @Valid
+ @NotNull(groups = First.class)
+ private Author author;
+
+ @SuppressWarnings("unused")
+ @NotNull
+ private int uselessField;
+
+ private int unconstraintField;
+
+ public int getUnconstraintField() {
+ return unconstraintField;
+ }
+
+ public void setUnconstraintField(int unconstraintField) {
+ this.unconstraintField = unconstraintField;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getSubtitle() {
+ return subtitle;
+ }
+
+ public void setSubtitle(String subtitle) {
+ this.subtitle = subtitle;
+ }
+
+ public Author getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(Author author) {
+ this.author = author;
+ }
+
+ @GroupSequence(value = {First.class, Second.class, Last.class})
+ public interface All {
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/BusinessAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/BusinessAddress.java
new file mode 100644
index 0000000..2921041
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/BusinessAddress.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessAddress extends Address {
+ private String company;
+
+ @NotNull
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/CompanyAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/CompanyAddress.java
new file mode 100644
index 0000000..02973a6
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/CompanyAddress.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import org.apache.bval.constraints.CompanyEmail;
+
+/**
+ * Description: <br/>
+ */
+public class CompanyAddress {
+ @SuppressWarnings("unused")
+ @CompanyEmail
+ private String email;
+
+ public CompanyAddress() {
+ }
+
+ public CompanyAddress(String email) {
+ this.email = email;
+ }
+
+ // do not provided getters & setters to test that value access
+ // of combined constraints directly use the private field 'email'
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Continent.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Continent.java
new file mode 100644
index 0000000..78be5c3
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Continent.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bval.jsr303.example;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A continent has a name and a set of {@link Country}s.
+ *
+ * @author Carlos Vara
+ */
+public class Continent {
+
+ @NotNull
+ public String name;
+
+ @Valid
+ public Set<Country> countries = new HashSet<Country>();
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Country.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Country.java
new file mode 100644
index 0000000..ffb408d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Country.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+public class Country {
+ @NotNull
+ private String name;
+ @Size(max = 2)
+ private String ISO2Code;
+ @Size(max = 3)
+ private String ISO3Code;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getISO2Code() {
+ return ISO2Code;
+ }
+
+ public void setISO2Code(String ISO2Code) {
+ this.ISO2Code = ISO2Code;
+ }
+
+ public String getISO3Code() {
+ return ISO3Code;
+ }
+
+ public void setISO3Code(String ISO3Code) {
+ this.ISO3Code = ISO3Code;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Customer.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Customer.java
new file mode 100644
index 0000000..9e74f1c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Customer.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.Email;
+import org.apache.bval.constraints.Password;
+
+import javax.validation.constraints.NotNull;
+
+public class Customer implements Person {
+ private String firstName;
+ private String middleName;
+ private String lastName;
+ @NotNull
+ private String customerId;
+ @Password(robustness = 5)
+ private String password;
+
+ @Email
+ private String emailAddress;
+
+ public String getEmailAddress() {
+ return emailAddress;
+ }
+
+ public void setEmailAddress(String emailAddress) {
+ this.emailAddress = emailAddress;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getMiddleName() {
+ return middleName;
+ }
+
+ public void setMiddleName(String middleName) {
+ this.middleName = middleName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public String getCustomerId() {
+ return customerId;
+ }
+
+ public void setCustomerId(String customerId) {
+ this.customerId = customerId;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Employee.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Employee.java
new file mode 100644
index 0000000..4efe01d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Employee.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public class Employee implements Person {
+ private String firstName, lastName;
+
+ public Employee(String firstN, String lastN) {
+ this.firstName = firstN;
+ this.lastName = lastN;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public String getMiddleName() {
+ return null; // not supported
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Engine.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Engine.java
new file mode 100644
index 0000000..071cb3d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Engine.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import javax.validation.constraints.Pattern;
+
+public class Engine {
+ @Pattern.List({
+ @Pattern(regexp = "^[A-Z0-9-]+$", flags = Pattern.Flag.CASE_INSENSITIVE,
+ message = "must contain alphabetical characters only"),
+ @Pattern(
+ regexp = "^....-....-....$", message = "must match ....-....-....")})
+ public String serialNumber;
+
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/First.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/First.java
new file mode 100644
index 0000000..d8f688f
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/First.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface First {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/FrenchAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/FrenchAddress.java
new file mode 100644
index 0000000..cfa468b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/FrenchAddress.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import org.apache.bval.constraints.FrenchZipCode;
+
+/**
+ * Description: <br/>
+ */
+public class FrenchAddress {
+ @FrenchZipCode(size = 7)
+ String zipCode;
+
+ public FrenchAddress() {
+ }
+
+ public FrenchAddress(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/IllustratedBook.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/IllustratedBook.java
new file mode 100644
index 0000000..89993ad
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/IllustratedBook.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Add a non-cascaded bean to a book.
+ *
+ * @version $Rev: 1004764 $ $Date: 2010-10-05 13:35:42 -0500 (Tue, 05 Oct 2010) $
+ */
+public class IllustratedBook extends Book {
+ private Person illustrator;
+
+ /**
+ * @return the illustrator
+ */
+ public Person getIllustrator() {
+ return illustrator;
+ }
+
+ /**
+ * @param illustrator
+ * the illustrator to set
+ */
+ public void setIllustrator(Person illustrator) {
+ this.illustrator = illustrator;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Last.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Last.java
new file mode 100644
index 0000000..558a60a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Last.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface Last {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Library.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Library.java
new file mode 100644
index 0000000..b6d3306
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Library.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+/**
+ * Description: <br/>
+ */
+public class Library {
+ @NotNull
+ private String libraryName;
+ @Valid
+ private final Map<String, Book> taggedBooks = new HashMap<String, Book>();
+
+ private Person[] persons;
+
+ public String getLibraryName() {
+ return libraryName;
+ }
+
+ public void setLibraryName(String libraryName) {
+ this.libraryName = libraryName;
+ }
+
+ public Map<String, Book> getTaggedBooks() {
+ return taggedBooks;
+ }
+
+ public Person[] getPersons() {
+ return persons;
+ }
+
+ public void setPersons(Person[] persons) {
+ this.persons = persons;
+ }
+
+ @Valid
+ public List<Employee> getEmployees() {
+ if (persons == null)
+ return Collections.emptyList();
+
+ ArrayList<Employee> emps = new ArrayList<Employee>(persons.length);
+ for (Person each : persons) {
+ if (each instanceof Employee)
+ emps.add((Employee) each);
+ }
+ return emps;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/MaxTestEntity.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/MaxTestEntity.java
new file mode 100644
index 0000000..3af3f99
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/MaxTestEntity.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import javax.validation.constraints.Max;
+import java.math.BigDecimal;
+
+/**
+ * Description: <br/>
+ */
+public class MaxTestEntity {
+ @Max(100)
+ private String text;
+ private String property;
+
+ @Max(300)
+ private long longValue;
+
+ private BigDecimal decimalValue;
+
+ public String getText() {
+ return text;
+ }
+
+ @Max(200)
+ public String getProperty() {
+ return property;
+ }
+
+ public long getLongValue() {
+ return longValue;
+ }
+
+ @Max(400)
+ public BigDecimal getDecimalValue() {
+ return decimalValue;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public void setLongValue(long longValue) {
+ this.longValue = longValue;
+ }
+
+ public void setDecimalValue(BigDecimal decimalValue) {
+ this.decimalValue = decimalValue;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/NoValidatorTestEntity.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/NoValidatorTestEntity.java
new file mode 100644
index 0000000..206ac7c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/NoValidatorTestEntity.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import javax.validation.constraints.Max;
+
+/**
+ * Description: <br/>
+ */
+public class NoValidatorTestEntity {
+ @SuppressWarnings("unused")
+ @Max(20)
+ private Object anything;
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Person.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Person.java
new file mode 100644
index 0000000..b6012d7
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Person.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import org.apache.bval.constraints.NotEmpty;
+
+public interface Person {
+ @NotEmpty
+ String getFirstName();
+
+ String getMiddleName();
+
+ @NotEmpty
+ String getLastName();
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/PreferredGuest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/PreferredGuest.java
new file mode 100644
index 0000000..ff9f10c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/PreferredGuest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import javax.validation.constraints.Digits;
+
+public class PreferredGuest extends Customer {
+ @Digits(integer = 10, fraction = 0)
+ private String guestCreditCardNumber;
+
+ public String getGuestCreditCardNumber() {
+ return guestCreditCardNumber;
+ }
+
+ public void setGuestCreditCardNumber(String guestCreditCardNumber) {
+ this.guestCreditCardNumber = guestCreditCardNumber;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/RecursiveFoo.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/RecursiveFoo.java
new file mode 100644
index 0000000..928a2f9
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/RecursiveFoo.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.Valid;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Description: <br/>
+ */
+public class RecursiveFoo {
+ @NotEmpty
+ @Valid
+ Collection<RecursiveFoo> foos = new ArrayList<RecursiveFoo>();
+
+ public Collection<RecursiveFoo> getFoos() {
+ return foos;
+ }
+
+ public void setFoos(Collection<RecursiveFoo> foos) {
+ this.foos = foos;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Second.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Second.java
new file mode 100644
index 0000000..e67cbee
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Second.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface Second {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/SizeTestEntity.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/SizeTestEntity.java
new file mode 100644
index 0000000..2211f2d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/SizeTestEntity.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+import javax.validation.constraints.Size;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Description: <br/>
+ */
+public class SizeTestEntity {
+ @Size(max=2)
+ public Map<String,String> map;
+ @Size(max=2)
+ public Collection<String> coll;
+ @Size(max=2)
+ public String text;
+
+ @Size(max=2)
+ public Object[] oa;
+ @Size(max=2)
+ public byte[] ba;
+ @Size(max=2)
+ public int[] it;
+ @Size(max=2)
+ public Integer[] oa2;
+ @Size(max=2)
+ public boolean[] boa;
+ @Size(max=2)
+ public char[] ca;
+ @Size(max=2)
+ public double[] da;
+ @Size(max=2)
+ public float[] fa;
+ @Size(max=2)
+ public long[] la;
+ @Size(max=2)
+ public short[] sa;
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/XmlEntitySampleBean.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/XmlEntitySampleBean.java
new file mode 100644
index 0000000..8ffefb0
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/XmlEntitySampleBean.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Description: bean used to test constraints described in XML<br/>
+ */
+public class XmlEntitySampleBean {
+ private String zipCode;
+ private String valueCode;
+
+ private String firstName;
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+ public String getValueCode() {
+ return valueCode;
+ }
+
+ public void setValueCode(String valueCode) {
+ this.valueCode = valueCode;
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/ZipCodeCityCarrier.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/ZipCodeCityCarrier.java
new file mode 100644
index 0000000..41fada4
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/ZipCodeCityCarrier.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface ZipCodeCityCarrier {
+ String getZipCode();
+ String getCity();
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/ExampleMethodService.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/ExampleMethodService.java
new file mode 100644
index 0000000..d3a2432
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/ExampleMethodService.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+/**
+ * Description: class with annotated methods to demonstrate
+ * method-level-validation<br/>
+ */
+public class ExampleMethodService {
+ public ExampleMethodService() {
+ }
+
+ public ExampleMethodService(@NotNull @NotEmpty String s1, @NotNull String s2) {
+ }
+
+ @NotNull
+ @NotEmpty
+ public String concat(@NotNull @NotEmpty String s1, @NotNull String s2) {
+ return s1 + s2;
+ }
+
+ public void save(@Pattern(regexp="[a-f0-9]{4}") String data) {
+ return;
+ }
+
+ @NotNull
+ @Size(min=3,max=10)
+ public String echo(@NotNull @Size(min=3,max=10) String str) {
+ return str;
+ }
+
+ public void personOp1(@Valid Person p) {
+ return;
+ }
+
+ public void personOp2(@NotNull @Valid Person p) {
+ return;
+ }
+
+ public static class Person {
+ @NotNull
+ String name;
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/MethodValidatorImplTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/MethodValidatorImplTest.java
new file mode 100644
index 0000000..21b33a7
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/MethodValidatorImplTest.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.jsr303.extensions;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr303.ApacheValidatorConfiguration;
+import org.apache.bval.jsr303.ClassValidator;
+import org.apache.bval.jsr303.extensions.ExampleMethodService.Person;
+
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * MethodValidatorImpl Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>11/11/2009</pre>
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class MethodValidatorImplTest extends TestCase {
+ public MethodValidatorImplTest(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ return new TestSuite(MethodValidatorImplTest.class);
+ }
+
+ public void testUnwrap() {
+ Validator v = getValidator();
+ ClassValidator cv = v.unwrap(ClassValidator.class);
+ assertTrue(v == cv);
+ assertTrue(v == v.unwrap(Validator.class));
+ MethodValidatorImpl mvi = v.unwrap(MethodValidatorImpl.class);
+ assertNotNull(mvi);
+ MethodValidator mv = v.unwrap(MethodValidator.class);
+ assertNotNull(mv);
+ assertTrue(mv == mv.unwrap(MethodValidatorImpl.class));
+ assertTrue(mv == mv.unwrap(ClassValidator.class));
+ }
+
+ public void testValidateMethodParameters() throws NoSuchMethodException {
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+ Method method =
+ service.getClass().getMethod("concat", new Class[]{String.class, String.class});
+ String[] params = new String[2];
+ params[0] = "Hello ";
+ params[1] = "world";
+ Set results = mv.validateParameters(service.getClass(), method, params);
+ assertEquals(true, results.isEmpty());
+
+ params[0] = "";
+ results = mv.validateParameters(service.getClass(), method, params);
+ assertEquals(1, results.size());
+
+ params[1] = null;
+ results = mv.validateParameters(service.getClass(), method, params);
+ assertEquals(2, results.size());
+
+ results = mv.validateParameter(service.getClass(), method, params[0], 0);
+ assertEquals(1, results.size());
+
+ results = mv.validateParameter(service.getClass(), method, "ok", 0);
+ assertEquals(0, results.size());
+ }
+
+ public void testValidateMoreMethodParameters() throws NoSuchMethodException {
+
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+ Method saveMethod = service.getClass().getMethod("save", new Class[]{String.class});
+
+ String[] saveParams = new String[1];
+ saveParams[0] = "abcd";
+
+ Set results = mv.validateParameters(service.getClass(), saveMethod, saveParams);
+ assertTrue(results.isEmpty());
+
+ saveParams[0] = "zzzz";
+ results = mv.validateParameters(service.getClass(), saveMethod, saveParams);
+ assertEquals(1, results.size());
+
+ Method echoMethod = service.getClass().getMethod("echo", new Class[]{String.class});
+
+ String[] echoParams = new String[1];
+ echoParams[0] = "hello";
+
+ results = mv.validateParameters(service.getClass(), echoMethod, echoParams);
+ assertTrue(results.isEmpty());
+
+ echoParams[0] = "h";
+ results = mv.validateParameters(service.getClass(), echoMethod, echoParams);
+ assertEquals(1, results.size());
+
+ echoParams[0] = null;
+ results = mv.validateParameters(service.getClass(), echoMethod, echoParams);
+ assertEquals(1, results.size());
+
+ }
+
+ public void testValidateConstructorParameters() throws NoSuchMethodException {
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+ Constructor constructor =
+ service.getClass().getConstructor(String.class, String.class);
+ String[] params = new String[2];
+ params[0] = "Hello ";
+ params[1] = "world";
+ Set results = mv.validateParameters(service.getClass(), constructor, params);
+ assertEquals(true, results.isEmpty());
+
+ params[0] = "";
+ results = mv.validateParameters(service.getClass(), constructor, params);
+ assertEquals(1, results.size());
+
+ params[1] = null;
+ results = mv.validateParameters(service.getClass(), constructor, params);
+ assertEquals(2, results.size());
+
+ results = mv.validateParameter(service.getClass(), constructor, params[0], 0);
+ assertEquals(1, results.size());
+
+ results = mv.validateParameter(service.getClass(), constructor, "ok", 0);
+ assertEquals(0, results.size());
+ }
+
+ public void testValidateReturnValue() throws NoSuchMethodException {
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+ Method method =
+ service.getClass().getMethod("concat", new Class[]{String.class, String.class});
+ Set results;
+ results = mv.validateReturnedValue(service.getClass(), method, "test");
+ assertEquals(true, results.isEmpty());
+
+ results = mv.validateReturnedValue(service.getClass(), method, "");
+ assertEquals(1, results.size());
+ }
+
+ public void testValidateMoreReturnValue() throws NoSuchMethodException {
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+ Method echoMethod = service.getClass().getMethod("echo", new Class[]{String.class});
+
+ String returnedValue = "a too long string";
+ Set results = mv.validateReturnedValue(service.getClass(), echoMethod, returnedValue);
+ assertEquals(1, results.size());
+
+ returnedValue = null;
+ results = mv.validateReturnedValue(service.getClass(), echoMethod, returnedValue);
+ assertEquals(1, results.size());
+
+ returnedValue = "valid";
+ results = mv.validateReturnedValue(service.getClass(), echoMethod, returnedValue);
+ assertTrue(results.isEmpty());
+ }
+
+ public void testValidateValidParam() throws NoSuchMethodException {
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+
+ Method personOp1 = service.getClass().getMethod("personOp1", new Class[]{Person.class});
+
+ // Validate with invalid person
+ Person p = new ExampleMethodService.Person();
+ Set<?> results = mv.validateParameters(service.getClass(), personOp1, new Object[]{p});
+ assertEquals("Expected 1 violation", 1, results.size());
+
+ // validate with valid person
+ p.name = "valid name";
+ results = mv.validateParameters(service.getClass(), personOp1, new Object[]{p});
+ assertTrue("No violations expected", results.isEmpty());
+
+ // validate with null person
+ results = mv.validateParameters(service.getClass(), personOp1, new Object[]{null});
+ assertTrue("No violations expected", results.isEmpty());
+ }
+
+ public void testValidateNotNullValidParam() throws NoSuchMethodException {
+ ExampleMethodService service = new ExampleMethodService();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+
+ Method personOp2 = service.getClass().getMethod("personOp2", new Class[]{Person.class});
+
+ // Validate with null person
+ Set<?> results = mv.validateParameters(service.getClass(), personOp2, new Object[]{null});
+ assertEquals("Expected 1 violation", 1, results.size());
+
+ // Validate with invalid person
+ Person p = new ExampleMethodService.Person();
+ results = mv.validateParameters(service.getClass(), personOp2, new Object[]{p});
+ assertEquals("Expected 1 violation", 1, results.size());
+
+ // validate with valid person
+ p.name = "valid name";
+ results = mv.validateParameters(service.getClass(), personOp2, new Object[]{p});
+ assertTrue("No violations expected", results.isEmpty());
+ }
+
+
+ /**
+ * Validate a method defined in an interface using the following combinations:
+ * <ul>
+ * <li>impl.class + impl.method</li>
+ * <li>interface.class + interface.method</li>
+ * <li>impl.class + interface.method</li>
+ * <li>interface.class + impl.method</li>
+ * </ul>
+ */
+ public void testValidateImplementedMethod() throws NoSuchMethodException {
+ UserMethodsImpl um = new UserMethodsImpl();
+ MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+
+ Method classMethod = um.getClass().getMethod("findUser", new Class[]{String.class, String.class, Integer.class});
+ Method ifaceMethod = UserMethods.class.getMethod("findUser", new Class[]{String.class, String.class, Integer.class});
+
+ Set<?> results;
+
+ // Validate from class (should create violations)
+ results = mv.validateParameters(um.getClass(), classMethod, new Object[]{"", "valid", null });
+ assertEquals("Invalid number of violations", 2, results.size());
+
+ // Validate from interface
+ results = mv.validateParameters(UserMethods.class, ifaceMethod, new Object[]{"", "valid", null });
+ assertEquals("Invalid number of violations", 0, results.size());
+
+ // Invalid combinations
+ try {
+ results = mv.validateParameters(UserMethods.class, classMethod, new Object[]{"", "valid", null });
+ Assert.fail("Exception not thrown when validating interface.class + impl.method");
+ } catch (ValidationException e) {
+ // Expected
+ }
+ try {
+ results = mv.validateParameters(um.getClass(), ifaceMethod, new Object[]{"", "valid", null });
+ Assert.fail("Exception not thrown when validating impl.class + interface.method");
+ } catch (ValidationException e) {
+ // Expected
+ }
+
+ }
+
+ public static interface UserMethods {
+ void findUser(String param1, String param2, Integer param3);
+ }
+
+ public static class UserMethodsImpl implements UserMethods {
+ // @Override - not allowed in 1.5 for Interface methods
+ public void findUser( @Size( min=1 ) String param1, @NotNull String param2, @NotNull Integer param3) {
+ return;
+ }
+ }
+
+
+ private Validator getValidator() {
+ return Validation
+ .byProvider(ApacheValidationProvider.class)
+ .configure()
+ .addProperty(ApacheValidatorConfiguration.Properties.METABEAN_FACTORY_CLASSNAMES,
+ MethodValidatorMetaBeanFactory.class.getName()).buildValidatorFactory().getValidator();
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/Billable.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/Billable.java
new file mode 100644
index 0000000..4db8fa4
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/Billable.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+/**
+ * Validation group checking a user is billable.
+ * Example 3.1. Definition of groups
+ */
+public interface Billable {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableCreditCard.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableCreditCard.java
new file mode 100644
index 0000000..11bebac
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableCreditCard.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+/**
+ * Example 3.2. Assign groups to constraints.
+ */
+public class BillableCreditCard {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableUser.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableUser.java
new file mode 100644
index 0000000..ddf3e57
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableUser.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+
+/**
+ * User representation
+ * Example 3.2. Assign groups to constraints.
+ */
+public class BillableUser {
+ @NotNull
+ private String firstname;
+
+ @NotNull(groups = Default.class)
+ private String lastname;
+
+ @NotNull(groups = {Billable.class, BuyInOneClick.class})
+ private BillableCreditCard defaultCreditCard;
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
+
+ public BillableCreditCard getDefaultCreditCard() {
+ return defaultCreditCard;
+ }
+
+ public void setDefaultCreditCard(BillableCreditCard defaultCreditCard) {
+ this.defaultCreditCard = defaultCreditCard;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BuyInOneClick.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BuyInOneClick.java
new file mode 100644
index 0000000..bd536bf
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BuyInOneClick.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+/**
+ * customer can buy without any harrassing checking process.
+ * Example 3.1. Definition of groups
+ */
+public interface BuyInOneClick {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java
new file mode 100644
index 0000000..d6e6ddf
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Author;
+import org.apache.bval.jsr303.example.Book;
+import org.apache.bval.jsr303.example.Country;
+import org.apache.bval.jsr303.example.Customer;
+import org.apache.bval.jsr303.example.Employee;
+import org.apache.bval.jsr303.example.Library;
+import org.apache.bval.jsr303.example.Person;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class CollectionValidationTest extends TestCase {
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ /**
+ * Validator instance to test
+ */
+ protected Validator validator;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ validator = createValidator();
+ }
+
+ /**
+ * Create the validator instance.
+ *
+ * @return Validator
+ */
+ protected Validator createValidator() {
+ return factory.getValidator();
+ }
+
+ public void testValidateList() {
+ Author author = new Author();
+ author.setFirstName("Peter");
+ author.setLastName("Ford");
+ author.setCompany("IBM");
+ author.setAddresses(new ArrayList<Address>());
+
+ Address adr1, adr2, adr3;
+ adr1 = new Address();
+ adr1.setCountry(new Country());
+ adr1.getCountry().setName("Germany");
+ adr1.setCity("Bonn");
+ adr1.setAddressline1("Strasse 1");
+
+ adr2 = new Address();
+ adr2.setCountry(new Country());
+ adr2.getCountry().setName("Cuba");
+ adr2.setCity("Habana");
+ adr2.setAddressline1("Calle 2");
+
+ adr3 = new Address();
+ adr3.setCountry(new Country());
+ adr3.getCountry().setName("USA");
+ adr3.setCity("San Francisco");
+ adr3.setAddressline1("Street 3");
+
+ author.getAddresses().add(adr1);
+ author.getAddresses().add(adr2);
+ author.getAddresses().add(adr3);
+
+ Set<ConstraintViolation<Author>> violations;
+
+ violations = validator.validate(author);
+ assertEquals(0, violations.size());
+
+ adr2.setCity(null); // violate not null
+ adr3.setAddressline1(null); // violate not null
+
+ violations = validator.validate(author);
+ assertEquals(2, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "addresses[1].city"));
+ assertNotNull(TestUtils.getViolation(violations, "addresses[2].addressline1"));
+ }
+
+ public void testValidateMapAndRedefinedDefaultGroupOnNonRootBean() {
+ Library lib = new Library();
+ lib.setLibraryName("Leibnitz Bibliothek");
+
+ Book book1, book2, book3;
+
+ book1 = new Book();
+ book1.setTitle("History of time");
+ book1.setSubtitle("How it really works");
+ Author hawking = new Author();
+ hawking.setFirstName("Stephen");
+ hawking.setFirstName("Hawking");
+ hawking.setAddresses(new ArrayList<Address>(1));
+ Address adr = new Address();
+ adr.setAddressline1("Street 1");
+ adr.setCity("London");
+ adr.setCountry(new Country());
+ adr.getCountry().setName("England");
+ hawking.getAddresses().add(adr);
+ book1.setAuthor(hawking);
+
+ book2 = new Book();
+ Author castro = new Author();
+ castro.setFirstName("Fidel");
+ castro.setLastName("Castro Ruz");
+ book2.setAuthor(castro);
+ book2.setTitle("My life");
+
+ book3 = new Book();
+ book3.setTitle("World best jokes");
+ Author someone = new Author();
+ someone.setFirstName("John");
+ someone.setLastName("Do");
+ book3.setAuthor(someone);
+
+ lib.getTaggedBooks().put("science", book1);
+ lib.getTaggedBooks().put("politics", book2);
+ lib.getTaggedBooks().put("humor", book3);
+
+ Set<ConstraintViolation<Library>> violations;
+
+ violations = validator.validate(lib);
+ assertTrue(violations.isEmpty());
+
+ book2.setTitle(null);
+ book3.getAuthor().setFirstName(""); // violate NotEmpty validation
+ book1.getAuthor().getAddresses().get(0).setCity(null);
+ /*
+ * This, by the way, tests redefined default group sequence behavior on
+ * non-root-beans (Library.Book)!!
+ */
+ violations = validator.validate(lib);
+ assertEquals("redefined default group of Book not correctly validated from Library", 3, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "taggedBooks[politics].title"));
+ assertNotNull(TestUtils.getViolation(violations, "taggedBooks[humor].author.firstName"));
+ assertNotNull(TestUtils.getViolation(violations, "taggedBooks[science].author.addresses[0].city"));
+ }
+
+ public void testValidateArray() {
+ Library lib = new Library();
+ lib.setLibraryName("Unibibliothek");
+ lib.setPersons(new Person[3]);
+ lib.getPersons()[0] = new Employee("Marcel", "Reich-Ranicki");
+ lib.getPersons()[1] = new Employee("Elke", "Heidenreich");
+ lib.getPersons()[2] = new Customer(); // not validated, because only
+ // getEmployees() is @Valid
+
+ Set<ConstraintViolation<Library>> violations;
+ violations = validator.validate(lib);
+ assertTrue(violations.isEmpty());
+
+ ((Employee) lib.getPersons()[1]).setFirstName(""); // violate NotEmpty
+ // constraint
+ violations = validator.validate(lib);
+ assertEquals(1, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "employees[1].firstName"));
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence.java
new file mode 100644
index 0000000..b2fa2bd
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence(value = CyclicGroupSequence.class)
+public interface CyclicGroupSequence {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence1.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence1.java
new file mode 100644
index 0000000..d976aab
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence1.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence(value = CyclicGroupSequence2.class)
+public interface CyclicGroupSequence1 {
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence2.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence2.java
new file mode 100644
index 0000000..fdd2d36
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence2.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence( value = CyclicGroupSequence1.class)
+public interface CyclicGroupSequence2 {
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/DefaultGroupSequenceTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/DefaultGroupSequenceTest.java
new file mode 100644
index 0000000..4b195ab
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/DefaultGroupSequenceTest.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import junit.framework.TestCase;
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.groups.Default;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Hardy Ferentschik
+ * @author Roman Stumm
+ */
+public class DefaultGroupSequenceTest extends TestCase {
+ public void testAssertDefaultGroupSequenceIsExpandableWithDefaultAtEndOfSequence() {
+ // create a dummy sequence
+ Group a = new Group(GroupA.class);
+ Group b = new Group(GroupB.class);
+ Group c = new Group(GroupC.class);
+ Group defaultGroup = new Group(Default.class);
+ List<Group> sequence = new ArrayList<Group>();
+ sequence.add(a);
+ sequence.add(b);
+ sequence.add(c);
+ sequence.add(defaultGroup);
+
+ Groups chain = new Groups();
+ chain.insertSequence(sequence);
+
+ // create test default sequence
+ List<Group> defaultSequence = new ArrayList<Group>();
+ defaultSequence.add(Group.DEFAULT);
+ defaultSequence.add(new Group(GroupA.class));
+ try {
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ fail();
+ } catch (GroupDefinitionException e) {
+ // success
+ }
+
+ defaultSequence.clear();
+ defaultSequence.add(new Group(GroupA.class));
+ defaultSequence.add(new Group(Default.class));
+ try {
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ fail();
+ } catch (GroupDefinitionException e) {
+ // success
+ }
+
+ defaultSequence.clear();
+ defaultSequence.add(Group.DEFAULT);
+ defaultSequence.add(new Group(GroupC.class));
+ try {
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ fail();
+ } catch (GroupDefinitionException e) {
+ // success
+ }
+
+ defaultSequence.clear();
+ defaultSequence.add(new Group(GroupC.class));
+ defaultSequence.add(Group.DEFAULT);
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ }
+
+
+ public void testAssertDefaulGroupSequenceIsExpandableWithDefaultAtBeginningOfSequence() {
+ // create a dummy sequence
+ Group a = new Group(GroupA.class);
+ Group b = new Group(GroupB.class);
+ Group c = new Group(GroupC.class);
+ Group defaultGroup = new Group(Default.class);
+ List<Group> sequence = new ArrayList<Group>();
+ sequence.add(defaultGroup);
+ sequence.add(a);
+ sequence.add(b);
+ sequence.add(c);
+
+ Groups chain = new Groups();
+ chain.insertSequence(sequence);
+
+ // create test default sequence
+ List<Group> defaultSequence = new ArrayList<Group>();
+ defaultSequence.add(Group.DEFAULT);
+ defaultSequence.add(new Group(GroupA.class));
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+
+
+ defaultSequence.clear();
+ defaultSequence.add(new Group(GroupA.class));
+ defaultSequence.add(Group.DEFAULT);
+ try {
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ fail();
+ } catch (GroupDefinitionException e) {
+ // success
+ }
+
+ defaultSequence.clear();
+ defaultSequence.add(Group.DEFAULT);
+ defaultSequence.add(new Group(GroupC.class));
+ try {
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ fail();
+ } catch (GroupDefinitionException e) {
+ // success
+ }
+
+ defaultSequence.clear();
+ defaultSequence.add(new Group(GroupC.class));
+ defaultSequence.add(Group.DEFAULT);
+ try {
+ chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+ fail();
+ } catch (GroupDefinitionException e) {
+ // success
+ }
+ }
+}
+
+interface TestSequence {
+}
+
+interface GroupA {
+}
+
+interface GroupB {
+}
+
+interface GroupC {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass1.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass1.java
new file mode 100644
index 0000000..35316f8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass1.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+/**
+ * Description: <br/>
+ */
+public class GClass1 implements GInterface1 {
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass2.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass2.java
new file mode 100644
index 0000000..ce1de51
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass2.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence({GClass1.class, GClass2.class})
+public class GClass2 extends GClass1 {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass3.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass3.java
new file mode 100644
index 0000000..4eb92c8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass3.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence({GClass3.class, GClass1.class})
+public class GClass3 {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GInterface1.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GInterface1.java
new file mode 100644
index 0000000..0f7c3f9
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GInterface1.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence(GInterface1.class)
+public interface GInterface1 {
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java
new file mode 100644
index 0000000..996a734
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Additional tests to check the correct processing of {@link GroupSequence}s
+ * by the validator.
+ *
+ * @author Carlos Vara
+ */
+public class GroupSequenceIsolationTest extends TestCase {
+
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator)factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ private Validator getValidator() {
+ return factory.getValidator();
+ }
+
+
+ /**
+ * When validating the {@link Default} group in a bean whose class doesn't
+ * define a {@link GroupSequence}, all the classes in the hierarchy must be
+ * checked for group sequence definitions and they must be evaluated in
+ * order for the constraints defined on those classes.
+ */
+ public void testGroupSequencesInHierarchyClasses() {
+ Validator validator = getValidator();
+
+ HolderWithNoGS h = new HolderWithNoGS();
+ Set<ConstraintViolation<HolderWithNoGS>> violations;
+
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a1");
+ good |= violation.getPropertyPath().toString().equals("b2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.a1 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ good |= violation.getPropertyPath().toString().equals("b2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.b2 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ good |= violation.getPropertyPath().toString().equals("b1");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.b1 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+ for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+ }
+
+ /**
+ * When validating the {@link Default} group in a bean whose class defines
+ * a group sequence, that group sequence is used for all the constraints.
+ */
+ public void testGroupSequenceOfBeanClass() {
+ Validator validator = getValidator();
+
+ HolderWithGS h = new HolderWithGS();
+ Set<ConstraintViolation<HolderWithGS>> violations;
+
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+ for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a1");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.a1 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+ for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("a2");
+ good |= violation.getPropertyPath().toString().equals("b2");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+
+ h.a2 = "good";
+ h.b2 = "good";
+ violations = validator.validate(h);
+ Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+ for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+ boolean good = violation.getPropertyPath().toString().equals("b1");
+ Assert.assertTrue("Wrong constraint", good);
+ }
+ }
+
+ @GroupSequence({GroupA1.class, A.class})
+ public static class A {
+ @NotNull(groups={GroupA1.class})
+ public String a1;
+ @NotNull
+ public String a2;
+ }
+
+ public static interface GroupA1 {
+ }
+
+ @GroupSequence({B.class, GroupB1.class})
+ public static class B extends A {
+ @NotNull(groups={GroupB1.class})
+ public String b1;
+ @NotNull
+ public String b2;
+ }
+
+ public static interface GroupB1 {
+
+ }
+
+ // No group sequence definition
+ public static class HolderWithNoGS extends B {
+
+ }
+
+ @GroupSequence({GroupA1.class, HolderWithGS.class, GroupB1.class})
+ public static class HolderWithGS extends B {
+
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceTest.java
new file mode 100644
index 0000000..0afcb38
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceTest.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+import org.apache.bval.jsr303.Jsr303Features;
+import org.apache.bval.jsr303.example.*;
+import org.apache.bval.jsr303.util.TestUtils;
+import org.apache.bval.model.MetaBean;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Description: test of group sequence behavior<br/>
+ */
+public class GroupSequenceTest extends TestCase {
+
+ static ValidatorFactory factory;
+
+ static {
+ factory = Validation.buildDefaultValidatorFactory();
+ ((DefaultMessageInterpolator)factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+ }
+
+ private Validator getValidator() {
+ return factory.getValidator();
+ }
+
+
+ public void testGroupSequence1() {
+ MetaBean metaBean =
+ ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+ .findForClass(GInterface1.class);
+ List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ Assert.assertNotNull(gseq);
+ Assert.assertEquals(1, gseq.size());
+ Assert.assertEquals(Group.DEFAULT, gseq.get(0));
+ }
+
+ public void testGroupSequence2() {
+ MetaBean metaBean =
+ ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+ .findForClass(GClass1.class);
+ List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ Assert.assertNotNull(gseq);
+ Assert.assertEquals(1, gseq.size());
+ Assert.assertEquals(Group.DEFAULT, gseq.get(0));
+ }
+
+ public void testGroupSequence3() {
+ MetaBean metaBean =
+ ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+ .findForClass(GClass2.class);
+ List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ Assert.assertNotNull(gseq);
+ Assert.assertEquals(2, gseq.size());
+ Assert.assertEquals(new Group(GClass1.class), gseq.get(0));
+ Assert.assertEquals(Group.DEFAULT, gseq.get(1));
+ }
+
+ public void testGroupSequence4() {
+ MetaBean metaBean =
+ ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+ .findForClass(GClass3.class);
+ List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+ Assert.assertNotNull(gseq);
+ Assert.assertEquals(2, gseq.size());
+ Assert.assertEquals(Group.DEFAULT, gseq.get(0));
+ Assert.assertEquals(new Group(GClass1.class), gseq.get(1));
+ }
+
+ public void testGroups() {
+ Validator validator = getValidator();
+
+ Author author = new Author();
+ author.setLastName("");
+ author.setFirstName("");
+ Book book = new Book();
+ book.setTitle("");
+ book.setAuthor(author);
+
+ Set<ConstraintViolation<Book>> constraintViolations =
+ validator.validate(book, First.class, Second.class, Last.class);
+ assertEquals("Wrong number of constraints", 3, constraintViolations.size());
+ assertNotNull(TestUtils.getViolation(constraintViolations, "title"));
+ assertNotNull(TestUtils.getViolation(constraintViolations, "author.firstName"));
+ assertNotNull(TestUtils.getViolation(constraintViolations, "author.lastName"));
+
+ author.setFirstName("Gavin");
+ author.setLastName("King");
+
+ constraintViolations = validator.validate(book, First.class, Second.class, Last.class);
+ ConstraintViolation<?> constraintViolation = constraintViolations.iterator().next();
+ assertEquals(1, constraintViolations.size());
+ assertEquals("may not be empty", constraintViolation.getMessage());
+ assertEquals(book, constraintViolation.getRootBean());
+ assertEquals(book.getTitle(), constraintViolation.getInvalidValue());
+ assertEquals("title", constraintViolation.getPropertyPath().toString());
+
+ book.setTitle("My fault");
+ book.setSubtitle("confessions of a president - a book for a nice price");
+
+ constraintViolations = validator.validate(book, First.class, Second.class, Last.class);
+ assertEquals(1, constraintViolations.size());
+ constraintViolation = constraintViolations.iterator().next();
+ assertEquals("size must be between 0 and 30", constraintViolation.getMessage());
+ assertEquals(book, constraintViolation.getRootBean());
+ assertEquals(book.getSubtitle(), constraintViolation.getInvalidValue());
+ assertEquals("subtitle", constraintViolation.getPropertyPath().toString());
+
+ book.setSubtitle("Capitalism in crisis");
+ author.setCompany("1234567890ß9876543212578909876542245678987432");
+
+ constraintViolations = validator.validate(book);
+ constraintViolation = constraintViolations.iterator().next();
+ assertEquals(1, constraintViolations.size());
+ assertEquals("size must be between 0 and 40", constraintViolation.getMessage());
+ assertEquals(book, constraintViolation.getRootBean());
+ assertEquals(author.getCompany(), constraintViolation.getInvalidValue());
+ assertEquals("author.company", constraintViolation.getPropertyPath().toString());
+
+ author.setCompany("apache");
+
+ constraintViolations = validator.validate(book, First.class, Second.class, Last.class);
+ assertEquals(0, constraintViolations.size());
+ }
+
+ public void testGroupSequence() {
+ Validator validator = getValidator();
+
+ Author author = new Author();
+ author.setLastName("");
+ author.setFirstName("");
+ Book book = new Book();
+ book.setAuthor(author);
+
+ Set<ConstraintViolation<Book>> constraintViolations =
+ validator.validate(book, Book.All.class);
+ assertEquals(2, constraintViolations.size());
+
+ author.setFirstName("Kelvin");
+ author.setLastName("Cline");
+
+ constraintViolations = validator.validate(book, Book.All.class);
+ ConstraintViolation<?> constraintViolation = constraintViolations.iterator().next();
+ assertEquals(1, constraintViolations.size());
+ assertEquals("may not be null", constraintViolation.getMessage());
+ assertEquals(book, constraintViolation.getRootBean());
+ assertEquals(book.getTitle(), constraintViolation.getInvalidValue());
+ assertEquals("title", constraintViolation.getPropertyPath().toString());
+
+ book.setTitle("247307892430798789024389798789");
+ book.setSubtitle("f43u rlök fjöq3liu opiur ölw3kj rölkj d");
+
+ constraintViolations = validator.validate(book, Book.All.class);
+ assertEquals(1, constraintViolations.size());
+ }
+
+
+ /**
+ * Check that when there is one constraint failure in one of the groups in
+ * a sequence, validation stops.
+ * JSR-303: 3.4.3
+ */
+ public void testValidationStopsWhenFailuresOnGroup() {
+ Validator validator = getValidator();
+
+ // Validate Dummy with its redefined Default group
+ Set<ConstraintViolation<Dummy>> violations = validator.validate(new Dummy());
+ assertEquals("Only 1 violation expected", 1, violations.size());
+ ConstraintViolation<Dummy> violation = violations.iterator().next();
+ assertEquals("Group1 should be evaluated first", "field1", violation.getPropertyPath().toString());
+ }
+
+ @GroupSequence({Dummy.Group1.class, Dummy.class})
+ public static class Dummy {
+
+ @NotNull(groups=Group1.class)
+ public String field1;
+
+ @NotNull
+ public String field2;
+
+ interface Group1 {
+ }
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupValidationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupValidationTest.java
new file mode 100644
index 0000000..79712ba
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupValidationTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: test features from spec chapter 3.4 group and group sequence<br/>
+ */
+public class GroupValidationTest extends TestCase {
+ private Validator validator;
+
+ protected void setUp() {
+ validator = ApacheValidatorFactory.getDefault().getValidator();
+ }
+
+ /**
+ * test spec: @NotNull on firstname and on lastname are validated when
+ * the Default group is validated.
+ */
+ public void testValidateFirstNameLastNameWithDefaultGroup() {
+ BillableUser user = new BillableUser();
+
+ Set<ConstraintViolation<BillableUser>> violations = validator.validate(user);
+ assertEquals(2, violations.size());
+ ConstraintViolation<?> violation = TestUtils.getViolation(violations, "firstname");
+ assertNotNull(violation);
+ assertEquals(user, violation.getRootBean());
+ violation = TestUtils.getViolation(violations, "lastname");
+ assertNotNull(violation);
+ assertEquals(user, violation.getRootBean());
+ }
+
+ /**
+ * test spec: @NotNull is checked on defaultCreditCard when either the
+ * Billable or BuyInOneClick group is validated.
+ */
+ public void testValidateDefaultCreditCardInBillableGroup() {
+ BillableUser user = new BillableUser();
+
+ Set<ConstraintViolation<BillableUser>> violations = validator.validate(user,
+ Billable.class);
+ assertEquals(1, violations.size());
+ ConstraintViolation<?> violation = TestUtils.getViolation(violations, "defaultCreditCard");
+ assertNotNull(violation);
+ assertEquals(user, violation.getRootBean());
+ }
+
+ public void testValidateDefaultCreditCardInBillableAndByInOneClickGroup() {
+ BillableUser user = new BillableUser();
+
+ Set<ConstraintViolation<BillableUser>> violations = validator.validate(user,
+ BuyInOneClick.class, Billable.class);
+ assertEquals(1, violations.size());
+ ConstraintViolation<?> violation = TestUtils.getViolation(violations, "defaultCreditCard");
+ assertNotNull(violation);
+ assertEquals(user, violation.getRootBean());
+ }
+
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupsComputerTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupsComputerTest.java
new file mode 100644
index 0000000..d393812
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupsComputerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.First;
+import org.apache.bval.jsr303.example.Last;
+import org.apache.bval.jsr303.example.Second;
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * GroupListComputer Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>04/09/2009</pre>
+ */
+public class GroupsComputerTest extends TestCase {
+ GroupsComputer groupsComputer;
+
+ public GroupsComputerTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ groupsComputer = new GroupsComputer();
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public static Test suite() {
+ return new TestSuite(GroupsComputerTest.class);
+ }
+
+ public void testComputeGroupsNotAnInterface() {
+ Set<Class<?>> groups = new HashSet<Class<?>>();
+ groups.add(String.class);
+ try {
+ groupsComputer.computeGroups(groups);
+ fail();
+ } catch (ValidationException ex) {
+
+ }
+ }
+
+ public void testGroupChainForNull() {
+ try {
+ groupsComputer.computeGroups((Class<?>[]) null);
+ fail();
+ } catch (IllegalArgumentException ex) {
+
+ }
+ }
+
+ public void testGroupChainForEmptySet() {
+ try {
+ groupsComputer.computeGroups(new HashSet<Class<?>>());
+ fail();
+ } catch (IllegalArgumentException ex) {
+
+ }
+ }
+
+ public void testCyclicGroupSequences() {
+ try {
+ Set<Class<?>> groups = new HashSet<Class<?>>();
+ groups.add(CyclicGroupSequence1.class);
+ groupsComputer.computeGroups(groups);
+ fail();
+ } catch (GroupDefinitionException ex) {
+
+ }
+ }
+
+ public void testCyclicGroupSequence() {
+ try {
+ Set<Class<?>> groups = new HashSet<Class<?>>();
+ groups.add(CyclicGroupSequence.class);
+ groupsComputer.computeGroups(groups);
+ fail();
+ } catch (GroupDefinitionException ex) {
+
+ }
+ }
+
+ public void testGroupDuplicates() {
+ Set<Class<?>> groups = new HashSet<Class<?>>();
+ groups.add(First.class);
+ groups.add(Second.class);
+ groups.add(Last.class);
+ Groups chain = groupsComputer.computeGroups(groups);
+ assertEquals(3, chain.groups.size());
+
+ groups.clear();
+ groups.add(First.class);
+ groups.add(First.class);
+ chain = groupsComputer.computeGroups(groups);
+ assertEquals(1, chain.groups.size());
+
+ groups.clear();
+ groups.add(First.class);
+ groups.add(Last.class);
+ groups.add(First.class);
+ chain = groupsComputer.computeGroups(groups);
+ assertEquals(2, chain.groups.size());
+ }
+
+
+ public void testSequenceResolution() {
+ Set<Class<?>> groups = new HashSet<Class<?>>();
+ groups.add(Address.Complete.class);
+ Groups chain = groupsComputer.computeGroups(groups);
+ Iterator<List<Group>> sequences = chain.getSequences().iterator();
+ List<Group> sequence = sequences.next();
+
+ assertEquals(Default.class, sequence.get(0).getGroup());
+ assertEquals(Address.HighLevelCoherence.class, sequence.get(1).getGroup());
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Auditable.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Auditable.java
new file mode 100644
index 0000000..75b0dca
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Auditable.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.implicit;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Auditable object contract.
+ * Example 3.7. Example of interface / group hosting constraints
+ */
+public interface Auditable {
+ @NotNull String getCreationDate();
+ @NotNull
+ String getLastUpdate();
+ @NotNull String getLastModifier();
+ @NotNull String getLastReader();
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/ImplicitGroupingTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/ImplicitGroupingTest.java
new file mode 100644
index 0000000..c91afc5
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/ImplicitGroupingTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.implicit;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: test spec chapter 3.4.4. Implicit grouping<br/>
+ */
+public class ImplicitGroupingTest extends TestCase {
+ private Validator validator;
+
+ protected void setUp() {
+ validator = ApacheValidatorFactory.getDefault().getValidator();
+ }
+
+ public void testValidateImplicitGrouping() {
+ Order order = new Order();
+ // When an Order object is validated on the Default group, ...
+ Set<ConstraintViolation<Order>> violations = validator.validate(order);
+ assertNotNull(TestUtils.getViolation(violations, "creationDate"));
+ assertNotNull(TestUtils.getViolation(violations, "lastUpdate"));
+ assertNotNull(TestUtils.getViolation(violations, "lastModifier"));
+ assertNotNull(TestUtils.getViolation(violations, "lastReader"));
+ assertNotNull(TestUtils.getViolation(violations, "orderNumber"));
+ assertEquals(5, violations.size());
+
+ // When an Order object is validated on the Auditable group, ...
+
+ /* Only the constraints present on Auditable (and any of its super interfaces)
+ and belonging to the Default group are validated
+ when the group Auditable is requested. */
+ violations = validator.validate(order, Auditable.class);
+ assertEquals("Implicit grouping not correctly implemented", 4, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "creationDate"));
+ assertNotNull(TestUtils.getViolation(violations, "lastUpdate"));
+ assertNotNull(TestUtils.getViolation(violations, "lastModifier"));
+ assertNotNull(TestUtils.getViolation(violations, "lastReader"));
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Order.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Order.java
new file mode 100644
index 0000000..fb6e7f8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Order.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.implicit;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * Represents an order in the system
+ */
+public class Order implements Auditable {
+ private String creationDate;
+ private String lastUpdate;
+ private String lastModifier;
+ private String lastReader;
+
+ private String orderNumber;
+
+ public String getCreationDate() {
+ return this.creationDate;
+ }
+
+ public String getLastUpdate() {
+ return this.lastUpdate;
+ }
+
+ public String getLastModifier() {
+ return this.lastModifier;
+ }
+
+ public String getLastReader() {
+ return this.lastReader;
+ }
+
+ @NotNull
+ @Size(min=10, max=10)
+ public String getOrderNumber() {
+ return this.orderNumber;
+ }
+
+ public void setCreationDate(String creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public void setLastUpdate(String lastUpdate) {
+ this.lastUpdate = lastUpdate;
+ }
+
+ public void setLastModifier(String lastModifier) {
+ this.lastModifier = lastModifier;
+ }
+
+ public void setLastReader(String lastReader) {
+ this.lastReader = lastReader;
+ }
+
+ public void setOrderNumber(String orderNumber) {
+ this.orderNumber = orderNumber;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BillableUser.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BillableUser.java
new file mode 100644
index 0000000..04e9af5
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BillableUser.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.inheritance;
+
+
+import org.apache.bval.jsr303.groups.Billable;
+import org.apache.bval.jsr303.groups.BillableCreditCard;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+
+/**
+ * Description: <br/>
+ */
+public class BillableUser {
+ @NotNull
+ private String firstname;
+
+ @NotNull(groups = Default.class)
+ private String lastname;
+
+ @NotNull(groups = {Billable.class})
+ private BillableCreditCard defaultCreditCard;
+
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
+
+ public BillableCreditCard getDefaultCreditCard() {
+ return defaultCreditCard;
+ }
+
+ public void setDefaultCreditCard(BillableCreditCard defaultCreditCard) {
+ this.defaultCreditCard = defaultCreditCard;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BuyInOneClick.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BuyInOneClick.java
new file mode 100644
index 0000000..744ad59
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BuyInOneClick.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.inheritance;
+
+
+import org.apache.bval.jsr303.groups.Billable;
+
+import javax.validation.groups.Default;
+
+/**
+ * Customer can buy without harrassing checking process.
+ * spec: Example 3.3. Groups can inherit other groups
+ */
+public interface BuyInOneClick extends Default, Billable {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/GroupInheritanceTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/GroupInheritanceTest.java
new file mode 100644
index 0000000..01a81b2
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/GroupInheritanceTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.inheritance;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: <br/>
+ */
+public class GroupInheritanceTest extends TestCase {
+ private Validator validator;
+
+ protected void setUp() {
+ validator = ApacheValidatorFactory.getDefault().getValidator();
+ }
+
+ /**
+ * validating the group BuyInOneClick will lead to the following constraints checking:
+ *<pre>
+ * * @NotNull on firstname and lastname
+ * * @NotNull on defaultCreditCard</pre>
+ * because Default and Billable are superinterfaces of BuyInOneClick.
+ */
+ public void testValidGroupBuyInOneClick() {
+ BillableUser user = new BillableUser();
+
+ Set<ConstraintViolation<BillableUser>> violations =
+ validator.validate(user, BuyInOneClick.class);
+ assertEquals(3, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "firstname"));
+ assertNotNull(TestUtils.getViolation(violations, "lastname"));
+ assertNotNull(TestUtils.getViolation(violations, "defaultCreditCard"));
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/Address.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/Address.java
new file mode 100644
index 0000000..e20cade
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/Address.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.redefining;
+
+
+import org.apache.bval.constraints.ZipCodeCityCoherence;
+import org.apache.bval.jsr303.example.ZipCodeCityCarrier;
+
+import javax.validation.GroupSequence;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * Example 3.6. Redefining Default group for Address:
+ * To redefine Default for a class, place a @GroupSequence annotation on the class ;
+ * this sequence expresses the sequence of groups that does
+ * substitute Default for this class.
+ */
+@GroupSequence({Address.class, Address.HighLevelCoherence.class, Address.ExtraCareful.class })
+@ZipCodeCityCoherence(groups = Address.HighLevelCoherence.class)
+public class Address implements ZipCodeCityCarrier {
+
+ /**
+ * check coherence on the overall object
+ * Needs basic checking to be green first
+ */
+ public interface HighLevelCoherence {}
+
+ /**
+ * Extra-careful validation group.
+ */
+ public interface ExtraCareful {}
+
+ @NotNull
+ @Size(max = 50, min = 1, groups = ExtraCareful.class)
+ private String street1;
+
+ @NotNull
+ private String zipCode;
+
+ @NotNull
+ @Size(max = 30)
+ private String city;
+
+ public String getStreet1() {
+ return street1;
+ }
+
+ public void setStreet1(String street1) {
+ this.street1 = street1;
+ }
+
+ public String getZipCode() {
+ return zipCode;
+ }
+
+ public void setZipCode(String zipCode) {
+ this.zipCode = zipCode;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/InvalidRedefinedDefaultGroupAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/InvalidRedefinedDefaultGroupAddress.java
new file mode 100644
index 0000000..162a811
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/InvalidRedefinedDefaultGroupAddress.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.redefining;
+
+import javax.validation.GroupSequence;
+import javax.validation.constraints.NotNull;
+
+
+/**
+ * If a @GroupSequence redefining the Default group for a class A does not
+ * contain the group A, a GroupDefinitionException is raised when the class is
+ * validated or when its metadata is requested.
+ */
+@GroupSequence({Address.class, Address.HighLevelCoherence.class})
+public class InvalidRedefinedDefaultGroupAddress {
+ @SuppressWarnings("unused")
+ @NotNull(groups = Address.HighLevelCoherence.class)
+ private String street;
+
+ @SuppressWarnings("unused")
+ @NotNull
+ private String city;
+
+ /**
+ * check coherence on the overall object
+ * Needs basic checking to be green first
+ */
+ public interface HighLevelCoherence {}
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/RedefiningDefaultGroupTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/RedefiningDefaultGroupTest.java
new file mode 100644
index 0000000..9295985
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/RedefiningDefaultGroupTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.groups.redefining;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.GroupDefinitionException;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: test Redefining the Default group for a class (spec. chapter 3.4.3)<br/>
+ */
+public class RedefiningDefaultGroupTest extends TestCase {
+ private Validator validator;
+
+ protected void setUp() {
+ validator = ApacheValidatorFactory.getDefault().getValidator();
+ }
+
+ /**
+ * when an address object is validated for the group Default,
+ * all constraints belonging to the group Default and hosted on Address are evaluated
+ */
+ public void testValidateDefaultGroup() {
+ Address address = new Address();
+ Set<ConstraintViolation<Address>> violations = validator.validate(address);
+ assertEquals(3, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "street1"));
+ assertNotNull(TestUtils.getViolation(violations, "zipCode"));
+ assertNotNull(TestUtils.getViolation(violations, "city"));
+
+ address.setStreet1("Elmstreet");
+ address.setZipCode("1234");
+ address.setCity("Gotham City");
+ violations = validator.validate(address);
+ assertTrue(violations.isEmpty());
+
+ violations = validator.validate(address, Address.HighLevelCoherence.class);
+ assertEquals(0, violations.size());
+
+ address.setCity("error");
+ violations = validator.validate(address, Address.HighLevelCoherence.class);
+ assertEquals(1, violations.size());
+
+ /**
+ * If none fails, all HighLevelCoherence constraints present on Address are evaluated.
+ *
+ * In other words, when validating the Default group for Address,
+ * the group sequence defined on the Address class is used.
+ */
+ violations = validator.validate(address);
+ assertEquals(
+ "redefined default group for Address must also validate HighLevelCoherence",
+ 1, violations.size());
+ }
+
+ public void testValidateProperty() {
+ Address address = new Address();
+ address.setStreet1("");
+ Set<ConstraintViolation<Address>> violations = validator.validateProperty(address, "street1");
+ //prove that ExtraCareful group was validated:
+ assertEquals(1, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "street1"));
+ }
+
+ public void testValidateValue() {
+ Set<ConstraintViolation<Address>> violations = validator.validateValue(Address.class, "street1", "");
+ //prove that ExtraCareful group was validated:
+ assertEquals(1, violations.size());
+ assertNotNull(TestUtils.getViolation(violations, "street1"));
+ }
+
+ public void testRaiseGroupDefinitionException() {
+ InvalidRedefinedDefaultGroupAddress address =
+ new InvalidRedefinedDefaultGroupAddress();
+ try {
+ validator.validate(address);
+ fail();
+ } catch (GroupDefinitionException ex) {
+
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/EnumerationConverterTestCase.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/EnumerationConverterTestCase.java
new file mode 100644
index 0000000..7bc3ecb
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/EnumerationConverterTestCase.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.beanutils.Converter;
+
+/**
+ * EnumerationConverter tester.
+ *
+ * $Id$
+ */
+public final class EnumerationConverterTestCase extends TestCase {
+
+ public EnumerationConverterTestCase(String name) {
+ super(name);
+ }
+
+ public void testEnum() {
+ Converter converter = EnumerationConverter.getInstance();
+
+ Thread.State expected = Thread.State.TERMINATED;
+ Thread.State actual = (Thread.State) converter.convert(Thread.State.class,
+ Thread.State.TERMINATED.name());
+ assertEquals(expected, actual);
+ }
+
+ public static Test suite() {
+ return new TestSuite(EnumerationConverterTestCase.class);
+ }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/PathImplTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/PathImplTest.java
new file mode 100644
index 0000000..fbd60fe
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/PathImplTest.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import java.util.Iterator;
+
+import javax.validation.Path;
+import javax.validation.ValidationException;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * PathImpl Tester.
+ *
+ * @version 1.0
+ * @since <pre>10/01/2009</pre>
+ */
+public class PathImplTest extends TestCase {
+ public PathImplTest(String name) {
+ super(name);
+ }
+
+ public void testParsing() {
+ String property = "order[3].deliveryAddress.addressline[1]";
+ Path path = PathImpl.createPathFromString(property);
+ assertEquals(property, path.toString());
+
+ Iterator<Path.Node> propIter = path.iterator();
+
+ assertTrue(propIter.hasNext());
+ Path.Node elem = propIter.next();
+ assertFalse(elem.isInIterable());
+ assertEquals("order", elem.getName());
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertTrue(elem.isInIterable());
+ assertEquals(new Integer(3), elem.getIndex());
+ assertEquals("deliveryAddress", elem.getName());
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertFalse(elem.isInIterable());
+ assertEquals(null, elem.getIndex());
+ assertEquals("addressline", elem.getName());
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertTrue(elem.isInIterable());
+ assertEquals(new Integer(1), elem.getIndex());
+ assertNull(elem.getName());
+
+ assertFalse(propIter.hasNext());
+ }
+
+ public void testParseMapBasedProperty() {
+ String property = "order[foo].deliveryAddress";
+ Path path = PathImpl.createPathFromString(property);
+ Iterator<Path.Node> propIter = path.iterator();
+
+ assertTrue(propIter.hasNext());
+ Path.Node elem = propIter.next();
+ assertFalse(elem.isInIterable());
+ assertEquals("order", elem.getName());
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertTrue(elem.isInIterable());
+ assertEquals("foo", elem.getKey());
+ assertEquals("deliveryAddress", elem.getName());
+
+ assertFalse(propIter.hasNext());
+ }
+
+ //some of the examples from the 1.0 bean validation spec, section 4.2
+ public void testSpecExamples() {
+ String fourthAuthor = "authors[3]";
+ Path path = PathImpl.createPathFromString(fourthAuthor);
+ Iterator<Path.Node> propIter = path.iterator();
+
+ assertTrue(propIter.hasNext());
+ Path.Node elem = propIter.next();
+ assertFalse(elem.isInIterable());
+ assertEquals("authors", elem.getName());
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertTrue(elem.isInIterable());
+ assertEquals(3, elem.getIndex().intValue());
+ assertNull(elem.getName());
+ assertFalse(propIter.hasNext());
+
+ String firstAuthorCompany = "authors[0].company";
+ path = PathImpl.createPathFromString(firstAuthorCompany);
+ propIter = path.iterator();
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertFalse(elem.isInIterable());
+ assertEquals("authors", elem.getName());
+
+ assertTrue(propIter.hasNext());
+ elem = propIter.next();
+ assertTrue(elem.isInIterable());
+ assertEquals(0, elem.getIndex().intValue());
+ assertEquals("company", elem.getName());
+ assertFalse(propIter.hasNext());
+ }
+
+ public void testNull() {
+ assertEquals(PathImpl.createPathFromString(null), PathImpl.create(null));
+
+ assertEquals("", PathImpl.create(null).toString());
+ Path path = PathImpl.create(null);
+ Path.Node node = path.iterator().next();
+ assertEquals(null, node.getName());
+ }
+
+ public void testUnbalancedBraces() {
+ try {
+ PathImpl.createPathFromString("foo[.bar");
+ fail();
+ } catch (ValidationException ex) {
+ }
+ }
+
+ public void testIndexInMiddleOfProperty() {
+ try {
+ PathImpl.createPathFromString("f[1]oo.bar");
+ fail();
+ } catch (ValidationException ex) {
+ }
+ }
+
+ public void testTrailingPathSeparator() {
+ try {
+ PathImpl.createPathFromString("foo.bar.");
+ fail();
+ } catch (ValidationException ex) {
+ }
+ }
+
+ public void testLeadingPathSeparator() {
+ try {
+ PathImpl.createPathFromString(".foo.bar");
+ fail();
+ } catch (ValidationException ex) {
+ }
+ }
+
+ public void testEmptyString() {
+ Path path = PathImpl.createPathFromString("");
+ assertEquals(null, path.iterator().next().getName());
+ }
+
+ public void testToString() {
+ PathImpl path = PathImpl.create(null);
+ path.addNode(new NodeImpl("firstName"));
+ assertEquals("firstName", path.toString());
+
+ path = PathImpl.create(null);
+ path.getLeafNode().setIndex(2);
+ assertEquals("[2]", path.toString());
+ path.addNode(new NodeImpl("firstName"));
+ assertEquals("[2].firstName", path.toString());
+ }
+
+ public void testAddRemoveNodes() {
+ PathImpl path = PathImpl.createPathFromString("");
+ assertTrue(path.isRootPath());
+ assertEquals(1, countNodes(path));
+ path.addNode(new NodeImpl("foo"));
+ assertFalse(path.isRootPath());
+ assertEquals(1, countNodes(path));
+ path.removeLeafNode();
+ assertTrue(path.isRootPath());
+ assertEquals(1, countNodes(path));
+ }
+
+ private int countNodes(Path path) {
+ int result = 0;
+ for (Iterator<Path.Node> iter = path.iterator(); iter.hasNext();) {
+ iter.next();
+ result++;
+ }
+ return result;
+ }
+
+ public static Test suite() {
+ return new TestSuite(PathImplTest.class);
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/TestUtils.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/TestUtils.java
new file mode 100644
index 0000000..30fcb4b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/TestUtils.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.util;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+
+import junit.framework.Assert;
+
+/**
+ * Description: <br/>
+ */
+public class TestUtils {
+ /**
+ * @param violations
+ * @param propertyPath
+ * - string format of a propertyPath
+ * @return the constraintViolation with the propertyPath's string
+ * representation given
+ */
+ public static <T> ConstraintViolation<T> getViolation(Set<ConstraintViolation<T>> violations, String propertyPath) {
+ for (ConstraintViolation<T> each : violations) {
+ if (each.getPropertyPath().toString().equals(propertyPath))
+ return each;
+ }
+ return null;
+ }
+
+ /**
+ * @param violations
+ * @param propertyPath
+ * @return count of violations
+ */
+ public static <T> int countViolations(Set<ConstraintViolation<T>> violations, String propertyPath) {
+ int result = 0;
+ for (ConstraintViolation<T> each : violations) {
+ if (each.getPropertyPath().toString().equals(propertyPath)) {
+ result++;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @param <T>
+ * @param violations
+ * @param message
+ * @return the constraint violation with the specified message found, if any
+ */
+ public static <T> ConstraintViolation<T> getViolationWithMessage(Set<ConstraintViolation<T>> violations,
+ String message) {
+ for (ConstraintViolation<T> each : violations) {
+ if (each.getMessage().equals(message))
+ return each;
+ }
+ return null;
+ }
+
+ /**
+ * assume set addition either does nothing, returning false per collection
+ * contract, or throws an Exception; in either case size should remain
+ * unchanged
+ *
+ * @param collection
+ */
+ public static void failOnModifiable(Collection<?> collection, String description) {
+ int size = collection.size();
+ try {
+ Assert
+ .assertFalse(String.format("should not permit modification to %s", description), collection.add(null));
+ } catch (Exception e) {
+ // okay
+ }
+ Assert.assertEquals("constraint descriptor set size changed", size, collection.size());
+ }
+
+ /**
+ * Assert that the specified ConstraintFinder provides constraints of each of the specified types.
+ * @param constraintFinder
+ * @param types
+ */
+ public static void assertConstraintTypesFound(ConstraintFinder constraintFinder, Class<? extends Annotation>... types) {
+ outer: for (Class<? extends Annotation> type : types) {
+ for (ConstraintDescriptor<?> descriptor : constraintFinder.getConstraintDescriptors()) {
+ if (descriptor.getAnnotation().annotationType().equals(type)) {
+ continue outer;
+ }
+ }
+ Assert.fail(String.format("Missing expected constraint descriptor of type %s", type));
+ }
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestConstraintValidatorFactory.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestConstraintValidatorFactory.java
new file mode 100644
index 0000000..b05f5ea
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestConstraintValidatorFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+import org.apache.bval.jsr303.DefaultConstraintValidatorFactory;
+
+/**
+ * Description: <br/>
+ */
+public class TestConstraintValidatorFactory extends DefaultConstraintValidatorFactory {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestMessageInterpolator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestMessageInterpolator.java
new file mode 100644
index 0000000..8c4418a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestMessageInterpolator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+
+/**
+ * Description: <br/>
+ */
+public class TestMessageInterpolator extends DefaultMessageInterpolator {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/ValidationParserTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/ValidationParserTest.java
new file mode 100644
index 0000000..b153b77
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/ValidationParserTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.bval.jsr303.xml;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr303.ApacheValidatorConfiguration;
+import org.apache.bval.jsr303.ConfigurationImpl;
+import org.apache.bval.jsr303.example.XmlEntitySampleBean;
+import org.apache.bval.jsr303.resolver.SimpleTraversableResolver;
+
+import javax.validation.*;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * ValidationParser Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>11/25/2009</pre>
+ */
+public class ValidationParserTest extends TestCase
+ implements ApacheValidatorConfiguration.Properties {
+ public ValidationParserTest(String name) {
+ super(name);
+ }
+
+ public void testGetInputStream() throws IOException {
+ ValidationParser vp = new ValidationParser("sample-validation.xml");
+ assertNotNull(vp.getInputStream("sample-validation.xml"));
+
+ try {
+ vp.getInputStream("META-INF/MANIFEST.MF"); // this is available in multiple jars hopefully
+ fail("exception not thrown");
+ } catch(ValidationException vex) {
+ assertTrue(vex.getMessage().startsWith("More than "));
+ }
+ }
+
+ public void testParse() {
+ ValidationParser vp = new ValidationParser("sample-validation.xml");
+ ConfigurationImpl config =
+ new ConfigurationImpl(null, new ApacheValidationProvider());
+ vp.processValidationConfig(config);
+ }
+
+ public void testConfigureFromXml() {
+ ValidatorFactory factory = getFactory();
+ assertTrue(factory.getMessageInterpolator() instanceof TestMessageInterpolator);
+ assertTrue(factory
+ .getConstraintValidatorFactory() instanceof TestConstraintValidatorFactory);
+ assertTrue(factory.getTraversableResolver() instanceof SimpleTraversableResolver);
+ Validator validator = factory.getValidator();
+ assertNotNull(validator);
+ }
+
+ private ValidatorFactory getFactory() {
+ ApacheValidatorConfiguration config =
+ Validation.byProvider(ApacheValidationProvider.class).configure();
+ config.addProperty(VALIDATION_XML_PATH, "sample-validation.xml");
+ return config.buildValidatorFactory();
+ }
+
+ public void testXmlEntitySample() {
+ XmlEntitySampleBean bean = new XmlEntitySampleBean();
+ bean.setFirstName("tooooooooooooooooooooooooooo long");
+ bean.setValueCode("illegal");
+ Validator validator = getFactory().getValidator();
+ Set<ConstraintViolation<XmlEntitySampleBean>> results = validator.validate(bean);
+ assertTrue(!results.isEmpty());
+ assertTrue(results.size() == 3);
+
+ bean.setZipCode("123");
+ bean.setValueCode("20");
+ bean.setFirstName("valid");
+ results = validator.validate(bean);
+ assertTrue(results.isEmpty());
+ }
+
+ public static Test suite() {
+ return new TestSuite(ValidationParserTest.class);
+ }
+}
diff --git a/trunk/bval-jsr303/src/test/resources/ValidationMessages.properties b/trunk/bval-jsr303/src/test/resources/ValidationMessages.properties
new file mode 100644
index 0000000..aad269b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/ValidationMessages.properties
@@ -0,0 +1,26 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# standard messages
+javax.validation.constraints.Pattern.message=must match "{regexp}"
+
+# custom messages (examples) for validation-api-1.0.Beta4
+test.validator.creditcard=credit card is not valid
+
+# custom messages (examples) for validation-api-1.0.CR1
+validator.creditcard=credit card is not valid
+
diff --git a/trunk/bval-jsr303/src/test/resources/java.policy b/trunk/bval-jsr303/src/test/resources/java.policy
new file mode 100644
index 0000000..98bb345
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/java.policy
@@ -0,0 +1,76 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+//
+// $Id$
+//
+// Allows unit tests to run with a Java Security Manager
+//
+// Cloned from https://svn.apache.org/repos/asf/commons/proper/lang/trunk/src/test/resources/java.policy
+//
+// <argLine>-Djava.security.manager -Djava.security.policy=${basedir}/src/test/resources/java.policy</argLine>
+//
+
+grant
+{
+ // let everyone read target dir
+ permission java.io.FilePermission "${user.dir}/target/-", "read";
+};
+
+// we don't care about the permissions of the testing infrastructure,
+// including maven;
+grant codeBase "file://${user.home}/.m2/repository/org/apache/maven/-"
+{
+ permission java.security.AllPermission;
+};
+
+// junit;
+grant codeBase "file://${user.home}/.m2/repository/junit/-"
+{
+ permission java.security.AllPermission;
+};
+
+// mockito;
+grant codeBase "file://${user.home}/.m2/repository/org/mockito/-"
+{
+ permission java.security.AllPermission;
+};
+
+// objenesis (via mockito);
+grant codeBase "file://${user.home}/.m2/repository/org/objenesis/-"
+{
+ permission java.security.AllPermission;
+};
+
+// and our own testcases
+grant codeBase "file://${user.dir}/target/test-classes/-"
+{
+ permission java.security.AllPermission;
+};
+
+grant codeBase "file://${user.home}/.m2/repository/org/apache/bval/-"
+{
+ permission java.lang.RuntimePermission "accessDeclaredMembers";
+ permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+};
+
+grant codeBase "file://${user.dir}/target/classes/-"
+{
+ permission java.lang.RuntimePermission "accessDeclaredMembers";
+ permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+ permission java.io.FilePermission "${user.home}/.m2/repository/-", "read";
+};
diff --git a/trunk/bval-jsr303/src/test/resources/sample-constraints.xml b/trunk/bval-jsr303/src/test/resources/sample-constraints.xml
new file mode 100644
index 0000000..75c405e
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/sample-constraints.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<constraint-mappings
+ xmlns="http://jboss.org/xml/ns/javax/validation/mapping"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation=
+ "http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd">
+ <default-package>org.apache.bval.jsr303.example</default-package>
+
+ <bean class="XmlEntitySampleBean" ignore-annotations="false">
+ <class ignore-annotations="true"/>
+ <field name="zipCode">
+ <!--@FrenchZipCode(size=3)-->
+ <constraint annotation="org.apache.bval.constraints.FrenchZipCode">
+ <element name="size">
+ <value>3</value>
+ </element>
+ </constraint>
+
+ </field>
+ <field name="valueCode">
+ <valid/>
+ <!-- @HasValue({ 0, 20 }) -->
+ <constraint annotation="org.apache.bval.constraints.HasValue">
+ <element name="value">
+ <value>0</value>
+ <value>20</value>
+ </element>
+ </constraint>
+
+ </field>
+ <getter name="firstName">
+ <valid/>
+ <!-- @Size(message="Size is limited",
+ groups={First.class, Default.class},
+ max=10
+ )
+ -->
+ <constraint annotation="javax.validation.constraints.Size">
+ <message>Size is limited</message>
+ <groups>
+ <value>org.apache.bval.jsr303.example.First</value>
+ <value>javax.validation.groups.Default</value>
+ </groups>
+ <element name="max">10</element>
+ </constraint>
+
+ </getter>
+
+ </bean>
+
+ <constraint-definition annotation="javax.validation.constraints.Size">
+ <validated-by include-existing-validators="false">
+ <value>org.apache.bval.constraints.SizeValidatorForString</value>
+ </validated-by>
+ </constraint-definition>
+
+</constraint-mappings>
diff --git a/trunk/bval-jsr303/src/test/resources/sample-validation.xml b/trunk/bval-jsr303/src/test/resources/sample-validation.xml
new file mode 100644
index 0000000..790a535
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/sample-validation.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<validation-config
+ xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation=
+ "http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">
+ <default-provider>org.apache.bval.jsr303.ApacheValidationProvider</default-provider>
+ <message-interpolator>org.apache.bval.jsr303.xml.TestMessageInterpolator</message-interpolator>
+ <traversable-resolver>org.apache.bval.jsr303.resolver.SimpleTraversableResolver</traversable-resolver>
+ <constraint-validator-factory>org.apache.bval.jsr303.xml.TestConstraintValidatorFactory</constraint-validator-factory>
+ <constraint-mapping>sample-constraints.xml</constraint-mapping>
+ <property name="test-prop">test-prop-value</property>
+</validation-config>
diff --git a/trunk/bval-tck/pom.xml b/trunk/bval-tck/pom.xml
new file mode 100644
index 0000000..60513fe
--- /dev/null
+++ b/trunk/bval-tck/pom.xml
@@ -0,0 +1,416 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-tck-runner</artifactId>
+ <name>Apache BVal :: bval-tck (TCK Runner)</name>
+ <description>Aggregates dependencies and runs the JSR-303 TCK</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>org.apache.bval.bundle</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <jboss.home>/opt/jboss-5.1.0.GA</jboss.home>
+ <validation.provider>org.apache.bval.jsr303.ApacheValidationProvider</validation.provider>
+ <remote.debug />
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <!-- Don't publish these artifacts to central -->
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ <pluginManagement>
+ <plugins>
+ <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>
+ org.apache.maven.plugins
+ </groupId>
+ <artifactId>
+ maven-dependency-plugin
+ </artifactId>
+ <versionRange>
+ [2.1,)
+ </versionRange>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>tck</id>
+ <activation>
+ <property>
+ <name>tck</name>
+ </property>
+ </activation>
+ <dependencies>
+ <!-- Use the geronimo-validation spec instead
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ </dependency>
+ -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-validation_1.0_spec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <classifier>jdk15</classifier>
+ <version>5.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate.jsr303.tck</groupId>
+ <artifactId>jsr303-tck</artifactId>
+ <version>1.0.6.GA</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.test-harness</groupId>
+ <artifactId>jboss-test-harness-jboss-as-51</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ </dependencies>
+ <repositories>
+ <repository>
+ <!-- override outdated URLs for jboss repo ids -->
+ <id>repository.jboss.org</id>
+ <name>JBoss Public Maven Repository Group</name>
+ <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+ <layout>default</layout>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ <checksumPolicy>fail</checksumPolicy>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ <updatePolicy>never</updatePolicy>
+ <checksumPolicy>warn</checksumPolicy>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>jboss</id>
+ <name>JBoss Public Maven Repository Group (again)</name>
+ <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+ <layout>default</layout>
+ <releases>
+ <enabled>true</enabled>
+ <updatePolicy>never</updatePolicy>
+ <checksumPolicy>fail</checksumPolicy>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ <updatePolicy>never</updatePolicy>
+ <checksumPolicy>warn</checksumPolicy>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>snapshots.jboss.org</id>
+ <url>https://repository.jboss.org/nexus/content/repositories/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>jboss-snapshots</id>
+ <url>https://repository.jboss.org/nexus/content/repositories/snapshots/</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <phase>generate-test-sources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <stripVersion>true</stripVersion>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.hibernate.jsr303.tck</groupId>
+ <artifactId>jsr303-tck</artifactId>
+ <type>xml</type>
+ <classifier>suite</classifier>
+ <overWrite>false</overWrite>
+ </artifactItem>
+ <!-- use geronimo-validation spec
+ <artifactItem>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ -->
+ <artifactItem>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-validation_1.0_spec</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>org.apache.bval.bundle</artifactId>
+ <version>${project.version}</version>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils-core</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <suiteXmlFiles>
+ <suiteXmlFile>${project.build.directory}/dependency/jsr303-tck-suite.xml</suiteXmlFile>
+ </suiteXmlFiles>
+ <argLine>-Xmx512m</argLine>
+ <forkMode>once</forkMode>
+ <systemProperties>
+ <property>
+ <name>validation.provider</name>
+ <value>${validation.provider}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-test-report</id>
+ <phase>test</phase>
+ <goals>
+ <goal>report-only</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <outputDirectory>${project.build.directory}/surefire-reports</outputDirectory>
+ <outputName>test-report</outputName>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>incontainer-debug</id>
+ <activation>
+ <property>
+ <name>debug</name>
+ </property>
+ </activation>
+ <properties>
+ <remote.debug>-Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
+ </remote.debug>
+ </properties>
+ </profile>
+
+ <profile>
+ <id>incontainer</id>
+ <activation>
+ <property>
+ <name>incontainer</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <suiteXmlFiles>
+ <suiteXmlFile>${project.build.directory}/dependency/jsr303-tck-suite.xml</suiteXmlFile>
+ </suiteXmlFiles>
+ <systemProperties>
+ <property>
+ <name>validation.provider</name>
+ <value>${validation.provider}</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.standalone</name>
+ <value>false</value>
+ </property>
+ <property>
+ <name>jboss.home</name>
+ <value>${jboss.home}</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.container.javaOpts</name>
+ <value>-Xms128m -Xmx512m -XX:MaxPermSize=256m -Dorg.jboss.resolver.warning=true
+ -Dvalidation.provider=${validation.provider} ${remote.debug}
+ </value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.container.forceRestart</name>
+ <value>true</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.runIntegrationTests</name>
+ <value>true</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.libraryDirectory</name>
+ <value>target/dependency/lib</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.outputDirectory</name>
+ <value>target</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>write-artifacts-to-disk</id>
+ <activation>
+ <property>
+ <name>dumpArtifacts</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-jsr-303-artifacts</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <classpathScope>test</classpathScope>
+ <mainClass>org.jboss.testharness.api.TCK</mainClass>
+ <systemProperties>
+ <systemProperty>
+ <key>dumpArtifacts</key>
+ <value>true</value>
+ </systemProperty>
+ <systemProperty>
+ <key>org.jboss.testharness.outputDirectory</key>
+ <value>target/jsr303-artifacts</value>
+ </systemProperty>
+ <systemProperty>
+ <key>org.jboss.testharness.libraryDirectory</key>
+ <value>target/jsr303-artifacts/dependencies</value>
+ </systemProperty>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
+
diff --git a/trunk/bval-tck/src/main/resources/META-INF/jboss-test-harness.properties b/trunk/bval-tck/src/main/resources/META-INF/jboss-test-harness.properties
new file mode 100644
index 0000000..895a5db
--- /dev/null
+++ b/trunk/bval-tck/src/main/resources/META-INF/jboss-test-harness.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+org.jboss.testharness.spi.StandaloneContainers=org.hibernate.jsr303.tck.util.StandaloneContainersImpl
+org.jboss.testharness.api.TestLauncher=org.jboss.testharness.impl.runner.servlet.ServletTestLauncher
+org.jboss.testharness.testPackage=org.hibernate.jsr303.tck.tests
\ No newline at end of file
diff --git a/trunk/bval-tck/src/test/resources/jndi.properties b/trunk/bval-tck/src/test/resources/jndi.properties
new file mode 100644
index 0000000..433246b
--- /dev/null
+++ b/trunk/bval-tck/src/test/resources/jndi.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#jboss JNDI properties
+java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
+java.naming.provider.url=jnp://localhost:1099
+java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
\ No newline at end of file
diff --git a/trunk/bval-tck/src/test/resources/log4j.xml b/trunk/bval-tck/src/test/resources/log4j.xml
new file mode 100644
index 0000000..0ce01ab
--- /dev/null
+++ b/trunk/bval-tck/src/test/resources/log4j.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <param name="Target" value="System.out"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <!-- The default pattern: Date Priority [Category] Message\n -->
+ <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{2}] %m%n"/>
+ </layout>
+ <filter class="org.apache.log4j.varia.StringMatchFilter">
+ <param name="AcceptOnMatch" value="false"/>
+ <param name="StringToMatch" value="Failure while notifying an observer of event [a]"/>
+ </filter>
+ <filter class="org.apache.log4j.varia.StringMatchFilter">
+ <param name="AcceptOnMatch" value="false"/>
+ <param name="StringToMatch"
+ value="Error destroying Request scoped unnamed simple bean org.jboss.jsr299.tck.tests.implementation.simple.lifecycle.Cod"/>
+ </filter>
+ </appender>
+
+ <!-- ############### Categories ################# -->
+ <category name="org.apache">
+ <priority value="INFO"/>
+ </category>
+
+ <category name="org.hibernate">
+ <priority value="INFO"/>
+ </category>
+
+ <category name="org.jboss.testharness">
+ <priority value="INFO"/>
+ </category>
+
+ <root>
+ <priority value="INFO"/>
+ <appender-ref ref="CONSOLE"/>
+ </root>
+
+</log4j:configuration>
diff --git a/trunk/bval-xstream/findbugs-exclude-filter.xml b/trunk/bval-xstream/findbugs-exclude-filter.xml
new file mode 100644
index 0000000..eaf41b1
--- /dev/null
+++ b/trunk/bval-xstream/findbugs-exclude-filter.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ This file contains some false positive bugs detected by findbugs. Their
+ false positive nature has been analyzed individually and they have been
+ put here to instruct findbugs it must ignore them.
+-->
+<FindBugsFilter>
+ <!-- not a problem -->
+ <Match>
+ <Class name="org.apache.bval.xml.XMLMetaBeanManager" />
+ <Method name="computeRelatedMetaBean" params="org.apache.bval.model.MetaProperty,java.lang.String" returns="void" />
+ <Bug pattern="NP_LOAD_OF_KNOWN_NULL_VALUE" />
+ </Match>
+</FindBugsFilter>
diff --git a/trunk/bval-xstream/pom.xml b/trunk/bval-xstream/pom.xml
new file mode 100644
index 0000000..d156a5e
--- /dev/null
+++ b/trunk/bval-xstream/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!--
+ Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <version>0.4</version>
+ </parent>
+
+ <artifactId>bval-xstream</artifactId>
+ <name>Apache BVal :: bval-xstream (optional)</name>
+ <packaging>jar</packaging>
+
+ <description>BVal XML Metadata with XStream</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ </dependency>
+ <!-- Test dependencies -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <!-- create mainClass attribute -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>default-jar</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.apache.bval.util.BValVersion</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </execution>
+ <execution>
+ <id>attach-tests</id>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <configuration>
+ <excludeFilterFile>findbugs-exclude-filter.xml</excludeFilterFile>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
+
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/EMailValidation.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/EMailValidation.java
new file mode 100644
index 0000000..9cd04f4
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/EMailValidation.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.routines;
+
+
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+import java.util.regex.Pattern;
+
+/**
+ * Description: example validation for email addresses using a regular expression<br/>
+ */
+public class EMailValidation implements Validation {
+
+ private java.util.regex.Pattern pattern = EMailValidationUtils.DEFAULT_EMAIL_PATTERN;
+
+ public <T extends ValidationListener> void validate(ValidationContext<T> context) {
+ if (context.getPropertyValue() == null) return;
+ if (!EMailValidationUtils.isValid(context.getPropertyValue(), getPattern())) {
+ context.getListener().addError(Reasons.EMAIL_ADDRESS, context);
+ }
+ }
+
+ public Pattern getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/NOPValidation.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/NOPValidation.java
new file mode 100644
index 0000000..6b63e3a
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/NOPValidation.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.routines;
+
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+/**
+ * Description: DO NOTHING VALIDATION (can be used to turn off standard validation)<br/>
+ */
+public class NOPValidation implements Validation {
+
+ public <T extends ValidationListener> void validate(ValidationContext<T> context) {
+ // do nothing
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/Reasons.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/Reasons.java
new file mode 100644
index 0000000..397081f
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/Reasons.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.routines;
+
+import org.apache.bval.model.Features;
+
+/**
+ * Description: StandardReasons for validation errors found in
+ * {@link org.apache.bval.ValidationResults}<br/>
+ */
+public interface Reasons extends Features.Property {
+ // The reasons inherited from Features are VALIDATION features only.
+ // INFO features are not meant to be validated.
+
+ // Add more reasons here.
+ String EMAIL_ADDRESS = "emailAddress";
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/StandardValidation.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/StandardValidation.java
new file mode 100644
index 0000000..c78f3b5
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/StandardValidation.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.routines;
+
+import static org.apache.bval.model.Features.Property.MANDATORY;
+import static org.apache.bval.model.Features.Property.MAX_LENGTH;
+import static org.apache.bval.model.Features.Property.MAX_VALUE;
+import static org.apache.bval.model.Features.Property.MIN_LENGTH;
+import static org.apache.bval.model.Features.Property.MIN_VALUE;
+import static org.apache.bval.model.Features.Property.REG_EXP;
+import static org.apache.bval.model.Features.Property.TIME_LAG;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.xml.XMLMetaValue;
+
+/**
+ * Description: This class implements the standard validations for properties!
+ * You can subclass this class and replace the implementation
+ * in the beanInfo-xml by providing it a validation "standard"<br/>
+ */
+public class StandardValidation implements Validation {
+
+ /** key for this validation in the validation list of the beanInfos */
+ public String getValidationId() {
+ return "standard";
+ }
+
+ public <T extends ValidationListener> void validate(ValidationContext<T> context) {
+ validateMandatory(context);
+ validateMaxLength(context);
+ validateMinLength(context);
+ validateMaxValue(context);
+ validateMinValue(context);
+ validateRegExp(context);
+ validateTimeLag(context);
+ }
+
+ protected <T extends ValidationListener> void validateTimeLag(ValidationContext<T> context) {
+ String lag = (String) context.getMetaProperty().getFeature(TIME_LAG);
+ if (lag == null) return;
+ if (context.getPropertyValue() == null) return;
+ long date = ((Date) context.getPropertyValue()).getTime();
+ long now = System.currentTimeMillis();
+ if (XMLMetaValue.TIMELAG_Future.equals(lag)) {
+ if (date < now) {
+ context.getListener().addError(TIME_LAG, context);
+ }
+ } else if (XMLMetaValue.TIMELAG_Past.equals(lag)) {
+ if (date > now) {
+ context.getListener().addError(TIME_LAG, context);
+ }
+ } else {
+ throw new IllegalArgumentException("unknown timelag " + lag + " at " + context);
+ }
+ }
+
+ private static final String REG_EXP_PATTERN = "cachedRegExpPattern";
+
+ protected <T extends ValidationListener> void validateRegExp(ValidationContext<T> context) {
+ final MetaProperty meta = context.getMetaProperty();
+ final String regExp = (String) meta.getFeature(REG_EXP);
+ if (regExp == null) return;
+ if (context.getPropertyValue() == null) return;
+
+ final String value = String.valueOf(context.getPropertyValue());
+ try {
+ Pattern pattern = (Pattern) meta.getFeature(REG_EXP_PATTERN);
+ if (pattern == null) {
+ pattern = Pattern.compile(regExp);
+ meta.putFeature(REG_EXP_PATTERN, pattern);
+ }
+ if (!pattern.matcher(value).matches()) {
+ context.getListener().addError(REG_EXP, context);
+ }
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException(
+ "regular expression malformed. regexp " + regExp + " at " + context, e);
+ }
+ }
+
+ protected <T extends ValidationListener> void validateMinValue(ValidationContext<T> context) {
+ @SuppressWarnings("unchecked")
+ Comparable<Object> minValue = (Comparable<Object>) context.getMetaProperty().getFeature(MIN_VALUE);
+ if (minValue == null || context.getPropertyValue() == null) return;
+ if (compare(context, minValue, context.getPropertyValue()) > 0) {
+ context.getListener().addError(MIN_VALUE, context);
+ }
+ }
+
+ protected <T extends ValidationListener> void validateMaxValue(ValidationContext<T> context) {
+ @SuppressWarnings("unchecked")
+ Comparable<Object> maxValue = (Comparable<Object>) context.getMetaProperty().getFeature(MAX_VALUE);
+ if (maxValue == null || context.getPropertyValue() == null) return;
+ if (compare(context, maxValue, context.getPropertyValue()) < 0) {
+ context.getListener().addError(MAX_VALUE, context);
+ }
+ }
+
+ private <T extends ValidationListener> int compare(ValidationContext<T> context, Comparable<Object> constraintValue,
+ Object currentValue) {
+ int r;
+ if (constraintValue.getClass().isAssignableFrom(currentValue.getClass())) {
+ r = constraintValue.compareTo(context.getPropertyValue());
+ } else if (currentValue instanceof Number) {
+ double dv = ((Number) currentValue).doubleValue();
+ double mdv = ((Number) constraintValue).doubleValue();
+ r = mdv > dv ? 1 : -1;
+ } else {
+ r = String.valueOf(constraintValue).compareTo(String.valueOf(currentValue));
+ }
+ return r;
+ }
+
+ protected <T extends ValidationListener> void validateMaxLength(ValidationContext<T> context) {
+ Integer maxLength = (Integer) context.getMetaProperty()
+ .getFeature(Features.Property.MAX_LENGTH);
+ if (maxLength == null) return;
+ if (context.getPropertyValue() == null) return;
+
+ final Object value = context.getPropertyValue();
+ int length = 0;
+ if (value instanceof String) {
+ length = ((String) value).length();
+ } else if (value instanceof Collection<?>) {
+ length = ((Collection<?>) value).size();
+ }
+ if (length > maxLength) {
+ context.getListener().addError(MAX_LENGTH, context);
+ }
+ }
+
+ protected <T extends ValidationListener> void validateMinLength(ValidationContext<T> context) {
+ Integer maxLength = (Integer) context.getMetaProperty()
+ .getFeature(Features.Property.MIN_LENGTH);
+ if (maxLength == null) return;
+ if (context.getPropertyValue() == null) return;
+
+ final Object value = context.getPropertyValue();
+ int length = 0;
+ if (value instanceof String) {
+ length = ((String) value).length();
+ } else if (value instanceof Collection<?>) {
+ length = ((Collection<?>) value).size();
+ }
+ if (length < maxLength) {
+ context.getListener().addError(MIN_LENGTH, context);
+ }
+ }
+
+ protected <T extends ValidationListener> void validateMandatory(ValidationContext<T> context) {
+ if (context.getMetaProperty().isMandatory()) {
+ if (context.getPropertyValue() == null) {
+ context.getListener().addError(MANDATORY, context);
+ }
+ }
+ }
+
+ public static StandardValidation getInstance() {
+ return new StandardValidation();
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/MetaBeanEnricher.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/MetaBeanEnricher.java
new file mode 100644
index 0000000..e7438b3
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/MetaBeanEnricher.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+
+import org.apache.bval.model.MetaBean;
+
+import java.util.Map;
+
+/**
+ * Description: Interface to merge meta beans<br/>
+ */
+public interface MetaBeanEnricher {
+
+ /**
+ * @param infos - the patches to apply
+ * @return all MetaBeans for classes that have a xml descriptor and
+ * additional the MetaBeans loaded by the given loaders.
+ * The given loaders may also return patches for MetaBeans that have
+ * also been returned by other loaders. The beans with patches for
+ * references to patched beans will be copied.
+ */
+ Map<String, MetaBean> enrichCopies(XMLMetaBeanInfos... infos);
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLFeaturesCapable.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLFeaturesCapable.java
new file mode 100644
index 0000000..3e9bf88
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLFeaturesCapable.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import org.apache.bval.model.FeaturesCapable;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ */
+public class XMLFeaturesCapable implements Serializable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ @XStreamImplicit
+ private List<XMLMetaFeature> features;
+ @XStreamImplicit(itemFieldName = "validator")
+ private List<XMLMetaValidatorReference> validators;
+
+ public List<XMLMetaFeature> getFeatures() {
+ return features;
+ }
+
+ public void setFeatures(List<XMLMetaFeature> features) {
+ this.features = features;
+ }
+
+ public void putFeature(String key, Object value) {
+ XMLMetaFeature anno = findFeature(key);
+ if (features == null) features = new ArrayList<XMLMetaFeature>();
+ if (anno == null) {
+ features.add(new XMLMetaFeature(key, value));
+ } else {
+ anno.setValue(value);
+ }
+ }
+
+ public void removeFeature(String key) {
+ XMLMetaFeature anno = findFeature(key);
+ if (anno != null) {
+ getFeatures().remove(anno);
+ }
+ }
+
+ public Object getFeature(String key) {
+ XMLMetaFeature anno = findFeature(key);
+ return anno == null ? null : anno.getValue();
+ }
+
+ private XMLMetaFeature findFeature(String key) {
+ if (features == null) return null;
+ for (XMLMetaFeature anno : features) {
+ if (key.equals(anno.getKey())) return anno;
+ }
+ return null;
+ }
+
+ public List<XMLMetaValidatorReference> getValidators() {
+ return validators;
+ }
+
+ public void setValidators(List<XMLMetaValidatorReference> validators) {
+ this.validators = validators;
+ }
+
+ public void addValidator(String validatorId) {
+ if (validators == null) validators = new ArrayList<XMLMetaValidatorReference>();
+ validators.add(new XMLMetaValidatorReference(validatorId));
+ }
+
+ public void mergeFeaturesInto(FeaturesCapable fc) {
+ if (getFeatures() != null) {
+ fc.optimizeRead(false);
+ for (XMLMetaFeature each : getFeatures()) {
+ fc.putFeature(each.getKey(), each.getValue());
+ }
+ fc.optimizeRead(true);
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMapper.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMapper.java
new file mode 100644
index 0000000..72284b7
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMapper.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.XStream;
+
+/**
+ * Description: <br/>
+ */
+public class XMLMapper {
+ private static final XMLMapper instance = new XMLMapper();
+
+ private final XStream xStream;
+
+ private XMLMapper() {
+ xStream = new XStream();
+ xStream.processAnnotations(new Class[] {XMLFeaturesCapable.class,
+ XMLMetaFeature.class, XMLMetaBean.class, XMLMetaBeanInfos.class,
+ XMLMetaBeanReference.class, XMLMetaElement.class, XMLMetaProperty.class,
+ XMLMetaValidator.class, XMLMetaValidatorReference.class});
+ xStream.setMode(XStream.NO_REFERENCES);
+ }
+
+ public static XMLMapper getInstance() {
+ return instance;
+ }
+
+ public XStream getXStream() {
+ return xStream;
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBean.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBean.java
new file mode 100644
index 0000000..9f516df
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBean.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("bean")
+public class XMLMetaBean extends XMLFeaturesCapable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAsAttribute()
+ private String id;
+ @XStreamAsAttribute()
+ private String name;
+ @XStreamAsAttribute()
+ private String impl;
+ @XStreamImplicit
+ private List<XMLMetaProperty> properties;
+ @XStreamImplicit
+ private List<XMLMetaBeanReference> beanRelations;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getImpl() {
+ return impl;
+ }
+
+ public void setImpl(String impl) {
+ this.impl = impl;
+ }
+
+ public List<XMLMetaProperty> getProperties() {
+ return properties;
+ }
+
+ public void setProperties(List<XMLMetaProperty> properties) {
+ this.properties = properties;
+ }
+
+ public void addProperty(XMLMetaProperty property) {
+ if (properties == null) properties = new ArrayList<XMLMetaProperty>();
+ properties.add(property);
+ }
+
+ public void putProperty(XMLMetaProperty property) {
+ if (property.getName() != null) {
+ XMLMetaProperty prop = findProperty(property.getName());
+ if (prop != null) {
+ properties.remove(prop);
+ }
+ }
+ addProperty(property);
+ }
+
+ public XMLMetaProperty removeProperty(String name) {
+ XMLMetaProperty prop = findProperty(name);
+ if (prop != null) {
+ properties.remove(prop);
+ }
+ return prop;
+ }
+
+ public XMLMetaProperty getProperty(String name) {
+ return findProperty(name);
+ }
+
+ private XMLMetaProperty findProperty(String name) {
+ if (properties == null) return null;
+ for (XMLMetaProperty prop : properties) {
+ if (name.equals(prop.getName())) return prop;
+ }
+ return null;
+ }
+
+ public List<XMLMetaBeanReference> getBeanRefs() {
+ return beanRelations;
+ }
+
+ public void setBeanRefs(List<XMLMetaBeanReference> beanRelations) {
+ this.beanRelations = beanRelations;
+ }
+
+
+ public void addBeanRef(XMLMetaBeanReference beanRelation) {
+ if (beanRelations == null) beanRelations = new ArrayList<XMLMetaBeanReference>();
+ beanRelations.add(beanRelation);
+ }
+
+ public void putBeanRef(XMLMetaBeanReference beanRelation) {
+ if (beanRelation.getName() != null) {
+ XMLMetaBeanReference relation = findBeanRef(beanRelation.getName());
+ if (relation != null) {
+ beanRelations.remove(relation);
+ }
+ }
+ addBeanRef(beanRelation);
+ }
+
+ public XMLMetaBeanReference removeBeanRef(String name) {
+ XMLMetaBeanReference relation = findBeanRef(name);
+ if (relation != null) {
+ beanRelations.remove(relation);
+ }
+ return relation;
+ }
+
+ public XMLMetaBeanReference getBeanRef(String name) {
+ return findBeanRef(name);
+ }
+
+ private XMLMetaBeanReference findBeanRef(String name) {
+ if (beanRelations == null) return null;
+ for (XMLMetaBeanReference relation : beanRelations) {
+ if (name.equals(relation.getName())) return relation;
+ }
+ return null;
+ }
+
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanBuilder.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanBuilder.java
new file mode 100644
index 0000000..8229183
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanBuilder.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import org.apache.bval.IntrospectorMetaBeanFactory;
+import org.apache.bval.MetaBeanBuilder;
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.model.MetaBean;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:10:46<br>
+ */
+public class XMLMetaBeanBuilder extends MetaBeanBuilder {
+ private XMLMetaBeanFactory xmlFactory;
+
+ public XMLMetaBeanBuilder(MetaBeanFactory[] factories) {
+ setFactories(factories);
+ }
+
+ public XMLMetaBeanBuilder() {
+ setFactories(new MetaBeanFactory[] { new IntrospectorMetaBeanFactory(), new XMLMetaBeanFactory() });
+ }
+
+ public void setFactories(MetaBeanFactory[] factories) {
+ super.setFactories(factories);
+ updateXmlFactory();
+ }
+
+ public void addLoader(XMLMetaBeanLoader loader) {
+ assertXmlFactory();
+ xmlFactory.addLoader(loader);
+ }
+
+ public MetaBean buildForId(String beanInfoId) throws Exception {
+ final XMLMetaBeanFactory.Visitor v;
+ assertXmlFactory();
+ xmlFactory.visitXMLBeanMeta(beanInfoId, v = new XMLMetaBeanFactory.Visitor() {
+ private MetaBean meta;
+
+ public MetaBean getMetaBean() {
+ return meta;
+ }
+
+ public void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos) throws Exception {
+ if (meta == null) {
+ meta = createMetaBean(xmlMeta);
+ }
+ xmlFactory.enrichMetaBean(meta, new XMLMetaBeanFactory.XMLResult(xmlMeta, xmlInfos));
+ }
+
+ });
+ if (v.getMetaBean() == null) {
+ throw new IllegalArgumentException("MetaBean " + beanInfoId + " not found");
+ }
+ return v.getMetaBean();
+ }
+
+ public Map<String, MetaBean> buildAll() throws Exception {
+ final Map<String, MetaBean> all = super.buildAll();
+ if (xmlFactory != null) {
+ xmlFactory.visitXMLBeanMeta(null, new XMLMetaBeanFactory.Visitor() {
+ public void visit(XMLMetaBean empty, XMLMetaBeanInfos xmlInfos) throws Exception {
+ if (xmlInfos.getBeans() == null)
+ return; // empty file, ignore
+ XMLMetaBeanFactory.XMLResult carrier = new XMLMetaBeanFactory.XMLResult(null, xmlInfos);
+
+ for (XMLMetaBean xmlMeta : xmlInfos.getBeans()) {
+ MetaBean meta = all.get(xmlMeta.getId());
+ if (meta == null) {
+ meta = createMetaBean(xmlMeta);
+ all.put(xmlMeta.getId(), meta);
+ }
+ carrier.xmlMeta = xmlMeta;
+ xmlFactory.enrichMetaBean(meta, carrier);
+ }
+ }
+
+ public MetaBean getMetaBean() {
+ return null; // do nothing
+ }
+ });
+ }
+ return all;
+ }
+
+ public Map<String, MetaBean> enrichCopies(Map<String, MetaBean> all, XMLMetaBeanInfos... infosArray)
+ throws Exception {
+ assertXmlFactory();
+ final Map<String, MetaBean> copies = new HashMap<String, MetaBean>(all.size());
+ boolean nothing = true;
+ XMLMetaBeanFactory.XMLResult carrier = new XMLMetaBeanFactory.XMLResult();
+ for (XMLMetaBeanInfos xmlMetaBeanInfos : infosArray) {
+ carrier.xmlInfos = xmlMetaBeanInfos;
+ if (xmlMetaBeanInfos == null)
+ continue;
+ try {
+ for (XMLMetaBean xmlMeta : xmlMetaBeanInfos.getBeans()) {
+ nothing = false;
+ MetaBean copy = copies.get(xmlMeta.getId());
+ if (copy == null) { // ist noch nicht kopiert
+ MetaBean meta = all.get(xmlMeta.getId());
+ if (meta == null) { // gibt es nicht
+ copy = createMetaBean(xmlMeta);
+ } else { // gibt es, jetzt kopieren
+ copy = meta.copy();
+ }
+ copies.put(xmlMeta.getId(), copy);
+ }
+ carrier.xmlMeta = xmlMeta;
+ xmlFactory.enrichMetaBean(copy, carrier);
+ }
+ } catch (IOException e) {
+ xmlFactory.handleLoadException(xmlMetaBeanInfos, e);
+ }
+ }
+ if (nothing)
+ return all;
+ for (Map.Entry<String, MetaBean> entry : all.entrySet()) {
+ /*
+ * alle unveraenderten werden AUCH KOPIERT (nur zwar nur, wegen
+ * potentieller CrossReferenzen durch Relationships)
+ */
+ if (!copies.containsKey(entry.getKey())) {
+ if (entry.getValue().hasRelationships()) {
+ copies.put(entry.getKey(), (MetaBean) entry.getValue().copy());
+ } else { // no relationship: do not clone()
+ copies.put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ return copies;
+ }
+
+ private MetaBean createMetaBean(XMLMetaBean xmlMeta) throws Exception {
+ return buildForClass(findLocalClass(xmlMeta.getImpl()));
+ }
+
+ private void updateXmlFactory() {
+ for (MetaBeanFactory each : getFactories()) {
+ if (each instanceof XMLMetaBeanFactory) { // use the first one!
+ xmlFactory = (XMLMetaBeanFactory) each;
+ return;
+ }
+ }
+ xmlFactory = null; // none
+ }
+
+ public XMLMetaBeanFactory getXmlFactory() {
+ return xmlFactory;
+ }
+
+ private void assertXmlFactory() {
+ if (xmlFactory == null) {
+ throw new IllegalStateException("no xmlFactory available");
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanFactory.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanFactory.java
new file mode 100644
index 0000000..83454ef
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanFactory.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import static org.apache.bval.model.Features.Property.JAVASCRIPT_VALIDATION_FUNCTIONS;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.model.FeaturesCapable;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.routines.StandardValidation;
+
+/**
+ * Description: Create or enrich MetaBeans from apache beanInfos xml<br/>
+ */
+public class XMLMetaBeanFactory implements MetaBeanFactory {
+ private static final Logger logger = Logger.getLogger(XMLMetaBeanFactory.class.getName());
+
+ // use LinkedHashMap to keep sequence of loaders
+ private final Map<XMLMetaBeanLoader, XMLMetaBeanInfos> resources =
+ new LinkedHashMap<XMLMetaBeanLoader, XMLMetaBeanInfos>();
+
+ private StandardValidation standardValidation = StandardValidation.getInstance();
+
+ public interface Visitor {
+ /**
+ * @param xmlMeta - null or the bean found
+ * @param xmlInfos - all infos in a single unit (xml file)
+ * @throws Exception
+ */
+ void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos) throws Exception;
+
+ MetaBean getMetaBean();
+ }
+
+ public static class XMLResult {
+ public XMLMetaBean xmlMeta;
+ public XMLMetaBeanInfos xmlInfos;
+
+ public XMLResult(XMLMetaBean metaBean, XMLMetaBeanInfos metaInfos) {
+ this.xmlMeta = metaBean;
+ this.xmlInfos = metaInfos;
+ }
+
+ public XMLResult() {
+ }
+ }
+
+ public void buildMetaBean(final MetaBean metaBean) throws Exception {
+ if(metaBean.getId() == null) return;
+ visitXMLBeanMeta(metaBean.getId(), new Visitor() {
+ public void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos)
+ throws Exception {
+ enrichMetaBean(metaBean, new XMLResult(xmlMeta, xmlInfos));
+ }
+
+ public MetaBean getMetaBean() {
+ return metaBean;
+ }
+ });
+ }
+
+ /** XMLMetaBeanLoader are used to know "locations" where to get BeanInfos from. */
+ public Collection<XMLMetaBeanLoader> getLoaders() {
+ return resources.keySet();
+ }
+
+ public void addLoader(XMLMetaBeanLoader loader) {
+ resources.put(loader, null);
+ }
+
+ public StandardValidation getStandardValidation() {
+ return standardValidation;
+ }
+
+ /** customize the implementation of standardValidation for this builder. */
+ public void setStandardValidation(StandardValidation standardValidation) {
+ this.standardValidation = standardValidation;
+ }
+
+ public void enrichMetaBean(MetaBean meta, XMLResult result) throws Exception {
+ if (result.xmlMeta.getId() != null) {
+ meta.setId(result.xmlMeta.getId());
+ }
+ if (result.xmlMeta.getName() != null) {
+ meta.setName(result.xmlMeta.getName());
+ }
+/* if (meta.getBeanClass() == null && result.xmlMeta.getImpl() != null) {
+ meta.setBeanClass(findLocalClass(result.xmlMeta.getImpl()));
+ }*/
+ result.xmlMeta.mergeFeaturesInto(meta);
+ enrichValidations(meta, result.xmlMeta, result, false);
+ if (result.xmlMeta.getProperties() != null) {
+ for (XMLMetaProperty xmlProp : result.xmlMeta.getProperties()) {
+ enrichElement(meta, xmlProp, result);
+ }
+ }
+ if (result.xmlMeta.getBeanRefs() != null) {
+ for (XMLMetaBeanReference xmlRef : result.xmlMeta.getBeanRefs()) {
+ enrichElement(meta, xmlRef, result);
+ }
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ protected void enrichValidations(FeaturesCapable prop, XMLFeaturesCapable xmlProp,
+ XMLResult result, boolean addStandard)
+ throws Exception {
+ if (xmlProp.getValidators() != null) {
+ // obsolete code? remove from here --->
+ String[] func = prop.getFeature(JAVASCRIPT_VALIDATION_FUNCTIONS);
+ List<String> jsValidators = new ArrayList<String>(
+ xmlProp.getValidators().size() + (func == null ? 0 : func.length));
+ if (func != null && func.length > 0) {
+ jsValidators.addAll(Arrays.asList(func));
+ } // <--- to here
+ boolean useStandard = prop instanceof MetaProperty;
+ for (XMLMetaValidatorReference valRef : xmlProp.getValidators()) {
+ if (standardValidation != null &&
+ valRef.getRefId().equals(standardValidation.getValidationId())) {
+ useStandard = false;
+ }
+ XMLMetaValidator validator =
+ result.xmlInfos.getValidator(valRef.getRefId());
+ if (validator != null) {
+ if (validator.getValidation() != null) {
+ prop.addValidation(validator.getValidation());
+ }
+ if (validator.getJsFunction() != null &&
+ !jsValidators.contains(validator.getJsFunction())) {
+ jsValidators.add(validator.getJsFunction());
+ }
+ }
+ }
+ if (!jsValidators.isEmpty()) {
+ prop.putFeature(JAVASCRIPT_VALIDATION_FUNCTIONS,
+ jsValidators.toArray(new String[jsValidators.size()]));
+ }
+ if (useStandard && standardValidation != null) {
+ if (!prop.hasValidation(standardValidation))
+ prop.addValidation(standardValidation);
+ }
+ } else if (addStandard && standardValidation != null &&
+ !prop.hasValidation(standardValidation)) {
+ prop.addValidation(standardValidation);
+ }
+ }
+
+ protected MetaProperty enrichElement(MetaBean meta, XMLMetaElement xmlProp,
+ XMLResult result) throws Exception {
+ MetaProperty prop = meta.getProperty(xmlProp.getName());
+ if (prop == null) {
+ prop = new MetaProperty();
+ prop.setName(xmlProp.getName());
+ meta.putProperty(xmlProp.getName(), prop);
+ }
+ xmlProp.mergeInto(prop);
+ enrichValidations(prop, xmlProp, result, true);
+ return prop;
+ }
+
+
+ public void visitXMLBeanMeta(String beanId, Visitor visitor) throws Exception {
+ for (Map.Entry<XMLMetaBeanLoader, XMLMetaBeanInfos> entry : resources
+ .entrySet()) {
+ if (entry.getValue() == null) {
+ // load when not already loaded
+ try {
+ entry.setValue(entry.getKey().load());
+ } catch (IOException e) {
+ handleLoadException(entry.getKey(), e);
+ }
+ }
+ if (entry.getValue() != null) { // search in loaded infos for the 'name'
+ if (beanId == null) {
+ visitor.visit(null, entry.getValue());
+ } else {
+ XMLMetaBean found = entry.getValue().getBean(beanId);
+ if (found != null) {
+ visitor.visit(found, entry.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * find a bean by the bean-id (=bean.name)
+ *
+ * @return null or the bean found from the first loader that has it.
+ */
+ protected XMLResult findXMLBeanMeta(String beanId) {
+ for (Map.Entry<XMLMetaBeanLoader, XMLMetaBeanInfos> entry : resources
+ .entrySet()) {
+ if (entry.getValue() == null) {
+ // load when not already loaded
+ try {
+ entry.setValue(entry.getKey().load());
+ } catch (IOException e) {
+ handleLoadException(entry.getKey(), e);
+ }
+ }
+ if (entry.getValue() != null) { // search in loaded infos for the 'name'
+ XMLMetaBean found = entry.getValue().getBean(beanId);
+ if (found != null) {
+ return new XMLResult(found, entry.getValue());
+ }
+ }
+ }
+ return null; // not found!
+ }
+
+ public void handleLoadException(Object loader, IOException e) {
+ logger.log(Level.SEVERE, String.format("Error loading %s", loader), e);
+ }
+
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanInfos.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanInfos.java
new file mode 100644
index 0000000..cd2ac88
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanInfos.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.bval.model.Validation;
+import org.apache.commons.lang3.ClassUtils;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
+
+/**
+ * Description: root element of a xml-beanInfos document<br/>
+ */
+@XStreamAlias("beanInfos")
+public class XMLMetaBeanInfos {
+ @XStreamAsAttribute
+ private String id;
+ @XStreamAsAttribute
+ private String version;
+ @XStreamImplicit
+ private List<XMLMetaValidator> validators;
+ @XStreamImplicit
+ private List<XMLMetaBean> beans;
+ @XStreamOmitField
+ private Map<String, XMLMetaBean> beanLookup;
+ @XStreamOmitField
+ private Map<String, XMLMetaValidator> validationLookup;
+
+ /**
+ * used for identification, may be empty, if there is no database origin for this object.
+ * could also contain a file-name - can be used flexible...
+ */
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ /**
+ * used for change-detection, when some other component caches MetaBeans based on this
+ * object. when the version changes, the cache could compare to its version state and recompute.
+ * can be used flexible...
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public List<XMLMetaValidator> getValidators() {
+ return validators;
+ }
+
+ public void setValidators(List<XMLMetaValidator> validators) {
+ this.validators = validators;
+ }
+
+ public List<XMLMetaBean> getBeans() {
+ return beans;
+ }
+
+ public void setBeans(List<XMLMetaBean> beans) {
+ this.beans = beans;
+ }
+
+ public XMLMetaBean getBean(String id) {
+ if (beans == null) return null;
+ if (beanLookup == null) initBeanLookup();
+ return beanLookup.get(id);
+ }
+
+ private void initBeanLookup() {
+ final HashMap<String, XMLMetaBean> map = new HashMap<String, XMLMetaBean>(beans.size());
+ for (XMLMetaBean bean : beans) {
+ map.put(bean.getId(), bean);
+ }
+ beanLookup = new ConcurrentHashMap<String, XMLMetaBean>(map);
+ }
+
+ private void initValidationLookup() throws Exception {
+ final HashMap<String, XMLMetaValidator> map = new HashMap<String, XMLMetaValidator>(validators.size());
+ for (XMLMetaValidator xv : validators) {
+ if (xv.getJava() != null) {
+ Validation validation =
+ (Validation) ClassUtils.getClass(xv.getJava()).newInstance();
+ xv.setValidation(validation);
+ map.put(xv.getId(), xv);
+ }
+ }
+ validationLookup = new ConcurrentHashMap<String, XMLMetaValidator>(map);
+ }
+
+ public void addBean(XMLMetaBean bean) {
+ if (beans == null) beans = new ArrayList<XMLMetaBean>();
+ beans.add(bean);
+ }
+
+ public XMLMetaValidator getValidator(String id) throws Exception {
+ if (validators == null) return null;
+ if (validationLookup == null) initValidationLookup();
+ return validationLookup.get(id);
+ }
+
+ public void addValidator(XMLMetaValidator validator) {
+ if (validators == null) validators = new ArrayList<XMLMetaValidator>();
+ validators.add(validator);
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanLoader.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanLoader.java
new file mode 100644
index 0000000..851c74e
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanLoader.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import java.io.IOException;
+
+/**
+ * Description: XMLMetaBeanLoader are used to know "locations" where to get BeanInfos from.<br/>
+ */
+public interface XMLMetaBeanLoader {
+ XMLMetaBeanInfos load() throws IOException;
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManager.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManager.java
new file mode 100644
index 0000000..0ab8d52
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManager.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import org.apache.bval.MetaBeanManager;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.PrivilegedActions;
+
+import java.util.Map;
+
+import static org.apache.bval.model.Features.Property.REF_BEAN_ID;
+
+/**
+ * Description: internal implementation class to construct metabeans with
+ * factories and from xstream xml files. You can register different
+ * XMLMetaBeanLoaders (see addLoader()) to register xstream-xml-files that
+ * contain meta-data. You can merge + unify meta data with method
+ * enrichCopies(). <br/>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 09:47:14<br>
+ */
+public class XMLMetaBeanManager extends MetaBeanManager implements XMLMetaBeanRegistry, MetaBeanEnricher {
+ public XMLMetaBeanManager() {
+ this(new XMLMetaBeanBuilder());
+ }
+
+ public XMLMetaBeanManager(XMLMetaBeanBuilder builder) {
+ super(builder);
+ }
+
+ public void addResourceLoader(String resource) {
+ addLoader(new XMLMetaBeanURLLoader(PrivilegedActions.getClassLoader(getClass()).getResource(resource)));
+ }
+
+ public synchronized void addLoader(XMLMetaBeanLoader loader) {
+ ((XMLMetaBeanBuilder) builder).addLoader(loader);
+ cache.clear(); // clear because new loaders can affect ALL MetaBeans
+ // already created!
+ complete = false;
+ }
+
+ /**
+ * @param infos
+ * - the patches to apply
+ * @return all MetaBeans for classes that have a xml descriptor and
+ * additional the MetaBeans loaded by the given loaders. The given
+ * loaders may also return patches for MetaBeans that have also been
+ * returned by other loaders. The beans with patches for references
+ * to patched beans will be copied.
+ */
+ public Map<String, MetaBean> enrichCopies(XMLMetaBeanInfos... infos) {
+ Map<String, MetaBean> cached = findAll();
+ try {
+ Map<String, MetaBean> patched = ((XMLMetaBeanBuilder) builder).enrichCopies(cached, infos);
+ for (Object entry : patched.values()) {
+ MetaBean meta = (MetaBean) entry;
+ computeRelationships(meta, patched);
+ }
+ return patched;
+ } catch (RuntimeException e) {
+ throw e; // do not wrap runtime exceptions
+ } catch (Exception e) {
+ throw new IllegalArgumentException("error enriching beanInfos", e);
+ }
+ }
+
+ /**
+ *
+ * @return all MetaBeans for classes that have a xml descriptor: key =
+ * bean.id, value = MetaBean
+ */
+ public Map<String, MetaBean> findAll() {
+ if (!complete) {
+ try {
+ Map<String, MetaBean> allBuilt = builder.buildAll();
+ for (MetaBean meta : allBuilt.values()) {
+ MetaBean cached = cache.findForId(meta.getId());
+ if (cached == null) {
+ cache.cache(meta);
+ }
+ }
+ Map<String, MetaBean> map = cache.findAll();
+ for (Object oentry : map.values()) {
+ MetaBean meta = (MetaBean) oentry;
+ computeRelationships(meta, map);
+ }
+ complete = true;
+ return map;
+ } catch (RuntimeException e) {
+ throw e; // do not wrap runtime exceptions
+ } catch (Exception e) {
+ throw new IllegalArgumentException("error creating beanInfos", e);
+ }
+ } else {
+ return cache.findAll();
+ }
+ }
+
+ protected void computeRelationships(MetaBean beanInfo, Map<String, MetaBean> cached) {
+ for (MetaProperty prop : beanInfo.getProperties()) {
+ String beanRef = (String) prop.getFeature(REF_BEAN_ID);
+ if (beanRef != null) {
+ prop.setMetaBean(cached.get(beanRef));
+ }
+ }
+ }
+
+ protected void computeRelatedMetaBean(MetaProperty prop, String beanRef) {
+ if (beanRef != null) {
+ prop.setMetaBean(findForId(beanRef));
+ } else {
+ super.computeRelatedMetaBean(prop, beanRef);
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManagerFactory.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManagerFactory.java
new file mode 100644
index 0000000..c7910e8
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManagerFactory.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import org.apache.bval.MetaBeanFinder;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:06:37<br>
+ */
+public class XMLMetaBeanManagerFactory {
+ private static XMLMetaBeanManager manager = new XMLMetaBeanManager();
+ /**
+ * global meta bean finder.
+ * @return
+ */
+ public static MetaBeanFinder getFinder() {
+ return manager;
+ }
+
+
+ /**
+ * set global meta bean manager, that is responsible
+ * for finding, caching, xml registry and enrichment algorithm.
+ * @param finder
+ */
+ public static void setManager(XMLMetaBeanManager finder) {
+ manager = finder;
+ }
+
+ /**
+ * global meta bean registry
+ * @return
+ */
+ public static XMLMetaBeanRegistry getRegistry() {
+ return manager;
+ }
+
+ /**
+ * global meta bean enricher
+ * @return
+ */
+ public static MetaBeanEnricher getEnricher() {
+ return manager;
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanReference.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanReference.java
new file mode 100644
index 0000000..b26fade
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanReference.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("relationship")
+public class XMLMetaBeanReference extends XMLMetaElement {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAsAttribute
+ private String beanId;
+
+ public XMLMetaBeanReference(String refId) {
+ this.beanId = refId;
+ }
+
+ public XMLMetaBeanReference() {
+ }
+
+ /** id of referenced target bean of the relationship */
+ public String getBeanId() {
+ return beanId;
+ }
+
+ public void setBeanId(String beanId) {
+ this.beanId = beanId;
+ }
+
+ @Override
+ public void mergeInto(MetaProperty prop) throws ClassNotFoundException {
+ super.mergeInto(prop); // call super!
+ if (getBeanId() != null) {
+ prop.putFeature(Features.Property.REF_BEAN_ID, getBeanId());
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanRegistry.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanRegistry.java
new file mode 100644
index 0000000..ef11f98
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanRegistry.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+/**
+ * Description: Interface of the object that holds all XMLMetaBeanLoaders <br/>
+ */
+public interface XMLMetaBeanRegistry {
+ /**
+ * add a loader for xml bean infos.
+ * the registry should use the loader in the sequence they have been added.
+ */
+ void addLoader(XMLMetaBeanLoader loader);
+
+ /**
+ * convenience method to add a loader for a xml file in the classpath
+ *
+ * @param resource - path of xml file in classpath
+ */
+ void addResourceLoader(String resource);
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanURLLoader.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanURLLoader.java
new file mode 100644
index 0000000..8dd735c
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanURLLoader.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Description: <br/>
+ */
+public class XMLMetaBeanURLLoader implements XMLMetaBeanLoader {
+ private final URL url;
+
+ public XMLMetaBeanURLLoader(URL url) {
+ if (url == null) throw new NullPointerException("URL required");
+ this.url = url;
+ }
+
+ public XMLMetaBeanInfos load() throws IOException {
+ InputStream stream = url.openStream();
+ try {
+ XMLMetaBeanInfos beanInfos = (XMLMetaBeanInfos) XMLMapper.getInstance()
+ .getXStream().fromXML(stream);
+ beanInfos.setId(url.toExternalForm());
+ return beanInfos;
+ } finally {
+ stream.close();
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaElement.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaElement.java
new file mode 100644
index 0000000..b753a79
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaElement.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import org.apache.bval.model.MetaProperty;
+import org.apache.commons.lang3.ClassUtils;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: <br/>
+ */
+public class XMLMetaElement extends XMLFeaturesCapable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAsAttribute()
+ private String name;
+ @XStreamAsAttribute()
+ private String mandatory;
+
+ @XStreamAsAttribute()
+ private Integer minLength;
+ @XStreamAsAttribute()
+ private Integer maxLength;
+ @XStreamAsAttribute()
+ private Boolean readonly;
+ @XStreamAsAttribute()
+ private Boolean hidden;
+ @XStreamAsAttribute()
+ private Boolean denied;
+ /**
+ * normally the type is determined by the implementation class.
+ * in case, no implementation class is given, the xml can
+ * contain the type directly.
+ */
+ @XStreamAsAttribute()
+ private String type;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getMandatory() {
+ return mandatory;
+ }
+
+ public void setMandatory(String mandatory) {
+ this.mandatory = mandatory;
+ }
+
+ public Integer getMinLength() {
+ return minLength;
+ }
+
+ public void setMinLength(Integer minLength) {
+ this.minLength = minLength;
+ }
+
+ public Integer getMaxLength() {
+ return maxLength;
+ }
+
+ public void setMaxLength(Integer maxLength) {
+ this.maxLength = maxLength;
+ }
+
+ public Boolean getReadonly() {
+ return readonly;
+ }
+
+ public void setReadonly(Boolean readonly) {
+ this.readonly = readonly;
+ }
+
+ public Boolean getDenied() {
+ return denied;
+ }
+
+ public void setDenied(Boolean denied) {
+ this.denied = denied;
+ }
+
+ public Boolean getHidden() {
+ return hidden;
+ }
+
+ public void setHidden(Boolean hidden) {
+ this.hidden = hidden;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void mergeInto(MetaProperty prop) throws ClassNotFoundException {
+ mergeFeaturesInto(prop);
+ if (getType() != null && getType().length() > 0) {
+ prop.setType(ClassUtils.getClass(getType())); // enhancement: or use getGenericType() ?
+ }
+ if (getHidden() != null) {
+ prop.putFeature(HIDDEN, getHidden().booleanValue());
+ }
+ if (getMandatory() != null) {
+ prop.putFeature(MANDATORY, getMandatory().equals("true"));
+ }
+ if (getMaxLength() != null) {
+ prop.putFeature(MAX_LENGTH, getMaxLength());
+ }
+ if (getMinLength() != null) {
+ prop.putFeature(MIN_LENGTH, getMinLength());
+ }
+ if (getReadonly() != null) {
+ prop.putFeature(READONLY, getReadonly());
+ }
+ if (getDenied() != null) {
+ prop.putFeature(DENIED, getDenied());
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaFeature.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaFeature.java
new file mode 100644
index 0000000..a500003
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaFeature.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+import java.io.Serializable;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("feature")
+public class XMLMetaFeature implements Serializable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAsAttribute
+ private String key;
+ @XStreamAsAttribute
+ private Object value;
+
+ public XMLMetaFeature(String key, Object value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public XMLMetaFeature() {
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaProperty.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaProperty.java
new file mode 100644
index 0000000..82c4772
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaProperty.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import org.apache.bval.model.MetaProperty;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("property")
+public class XMLMetaProperty extends XMLMetaElement {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * if you need other types (Long, double, String) for maxValue and
+ * minValue, configure via
+ * <pre>
+ * <feature key="maxValue">
+ * <value class="java.lang.Long">1000</value>
+ * </feature>
+ * </pre>
+ * instead with explicit typing.
+ */
+ @XStreamAsAttribute()
+ private Integer maxValue; // XStream requires a non-abstract type to parse XML
+ @XStreamAsAttribute()
+ private Integer minValue; // XStream requires a non-abstract type to parse XML
+
+ private String regexp;
+
+ @XStreamAsAttribute()
+ private String timeLag;
+
+
+ public Integer getMaxValue() {
+ return maxValue;
+ }
+
+ public void setMaxValue(Integer maxValue) {
+ this.maxValue = maxValue;
+ }
+
+ public Integer getMinValue() {
+ return minValue;
+ }
+
+ public void setMinValue(Integer minValue) {
+ this.minValue = minValue;
+ }
+
+ public String getRegexp() {
+ return regexp;
+ }
+
+ public void setRegexp(String regexp) {
+ this.regexp = regexp;
+ }
+
+ public String getTimeLag() {
+ return timeLag;
+ }
+
+ public void setTimeLag(String timeLag) {
+ this.timeLag = timeLag;
+ }
+
+ @Override
+ public void mergeInto(MetaProperty prop) throws ClassNotFoundException {
+ super.mergeInto(prop); // call super!
+ if (getMaxValue() != null) {
+ prop.putFeature(MAX_VALUE, getMaxValue());
+ }
+ if (getMinValue() != null) {
+ prop.putFeature(MIN_VALUE, getMinValue());
+ }
+ if (getRegexp() != null) {
+ prop.putFeature(REG_EXP, getRegexp());
+ }
+ if (getTimeLag() != null) {
+ prop.putFeature(TIME_LAG, getTimeLag());
+ }
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidator.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidator.java
new file mode 100644
index 0000000..399d8a4
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidator.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
+import org.apache.bval.model.Validation;
+
+import java.io.Serializable;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("validator")
+public class XMLMetaValidator implements Serializable {
+
+ @XStreamOmitField
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAsAttribute
+ private String id;
+ @XStreamAsAttribute
+ private String java; // implementation of Validation
+
+ @XStreamAsAttribute
+ private String jsFunction; // name of java script function
+
+ @XStreamOmitField
+ private transient Validation validation;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getJava() {
+ return java;
+ }
+
+ public void setJava(String java) {
+ this.java = java;
+ }
+
+ public void setValidation(Validation validation) {
+ this.validation = validation;
+ }
+
+ public Validation getValidation() {
+ return validation;
+ }
+
+ public String getJsFunction() {
+ return jsFunction;
+ }
+
+ public void setJsFunction(String jsFunction) {
+ this.jsFunction = jsFunction;
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidatorReference.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidatorReference.java
new file mode 100644
index 0000000..c6627c5
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidatorReference.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+import java.io.Serializable;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("validator-ref")
+public class XMLMetaValidatorReference implements Serializable {
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ @XStreamAsAttribute
+ private String refId;
+
+ public XMLMetaValidatorReference(String id) {
+ this.refId = id;
+ }
+
+ public XMLMetaValidatorReference() {
+ }
+
+ public String getRefId() {
+ return refId;
+ }
+
+ public void setRefId(String refId) {
+ this.refId = refId;
+ }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValue.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValue.java
new file mode 100644
index 0000000..9da06ec
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValue.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+/**
+ * Description: <br/>
+ */
+public interface XMLMetaValue {
+ // keys for Annotations
+ public static final String ANNOKEY_Widget = "WIDGET";
+ public static final String ANNOKEY_TableColumn = "TABLE_COLUMN";
+
+ // values for TIMELAG
+ public static final String TIMELAG_Past = "PAST";
+ public static final String TIMELAG_Future = "FUTURE";
+
+ // values for MANDATORY
+ public static final String MANDATORY = "true";
+ public static final String OPTIONAL = "false";
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessEnum.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessEnum.java
new file mode 100644
index 0000000..035771d
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessEnum.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.example;
+
+/**
+ * Description: <br/>
+ */
+public enum BusinessEnum {
+ VALUE1, VALUE2, VALUE3
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObject.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObject.java
new file mode 100644
index 0000000..4b511d5
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObject.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.example;
+
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessObject {
+ private long userId;
+ private int numericValue;
+ private String firstName, lastName, title;
+ private Date dateBirth;
+ private Timestamp validTo;
+ private String email;
+ private BusinessEnum choice;
+ private BusinessObjectAddress address;
+ private List<BusinessObjectAddress> addresses;
+
+ public BusinessEnum getChoice() {
+ return choice;
+ }
+
+ public void setChoice(BusinessEnum choice) {
+ this.choice = choice;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public Date getDateBirth() {
+ return dateBirth;
+ }
+
+ public void setDateBirth(Date dateBirth) {
+ this.dateBirth = dateBirth;
+ }
+
+ public Timestamp getValidTo() {
+ return validTo;
+ }
+
+ public void setValidTo(Timestamp validTo) {
+ this.validTo = validTo;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public BusinessObjectAddress getAddress() {
+ return address;
+ }
+
+ public void setAddress(BusinessObjectAddress address) {
+ this.address = address;
+ }
+
+ public void setNumericValue(int newNumericValue) {
+ numericValue = newNumericValue;
+ }
+
+ public int getNumericValue() {
+ return numericValue;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ public List<BusinessObjectAddress> getAddresses() {
+ return addresses;
+ }
+
+ public void setAddresses(List<BusinessObjectAddress> addresses) {
+ this.addresses = addresses;
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectAddress.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectAddress.java
new file mode 100644
index 0000000..9a6c18b
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectAddress.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.example;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessObjectAddress {
+ private String city, country;
+ private BusinessObject owner;
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public BusinessObject getOwner() {
+ return owner;
+ }
+
+ public void setOwner(BusinessObject owner) {
+ this.owner = owner;
+ }
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectBeanInfo.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectBeanInfo.java
new file mode 100644
index 0000000..61b06e5
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectBeanInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.example;
+
+import org.apache.bval.model.Features;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.beans.SimpleBeanInfo;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessObjectBeanInfo extends SimpleBeanInfo {
+ Class<?> targetClass = BusinessObject.class;
+
+ @Override
+ public BeanInfo[] getAdditionalBeanInfo() {
+ ExplicitBeanInfo bi = new ExplicitBeanInfo();
+ bi.setPropertyDescriptors(_getPropertyDescriptors());
+ return new BeanInfo[]{bi};
+ }
+
+ public PropertyDescriptor[] _getPropertyDescriptors() {
+ try {
+ PropertyDescriptor numericValue = new PropertyDescriptor("numericValue",
+ targetClass, "getNumericValue", "setNumericValue");
+ numericValue.setValue(Features.Property.MAX_VALUE, new Integer(100));
+ numericValue.setValue(Features.Property.MIN_VALUE, new Integer(-100));
+ return new PropertyDescriptor[]{numericValue};
+ } catch (IntrospectionException ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+}
+
+class ExplicitBeanInfo extends SimpleBeanInfo {
+ private PropertyDescriptor[] propertyDescriptors;
+
+ @Override
+ public PropertyDescriptor[] getPropertyDescriptors() {
+ return propertyDescriptors;
+ }
+
+ public void setPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
+ this.propertyDescriptors = propertyDescriptors;
+ }
+}
\ No newline at end of file
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/routines/StandardValidationTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/routines/StandardValidationTest.java
new file mode 100644
index 0000000..7bee3db
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/routines/StandardValidationTest.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.routines;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.BeanValidationContext;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.xml.XMLMetaValue;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * StandardValidation Tester.
+ */
+public class StandardValidationTest extends TestCase implements ValidationListener {
+ private StandardValidation validation;
+ private BeanValidationContext<StandardValidationTest> context;
+ private List<String> reasons = new ArrayList<String>();
+ private MetaProperty metaProperty;
+ private String stringValue;
+ private Date dateValue;
+ private int intValue;
+
+ public StandardValidationTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ validation = new StandardValidation();
+ context = new BeanValidationContext<StandardValidationTest>(this);
+ metaProperty = new MetaProperty();
+ context.setBean(this, null);
+ context.setMetaProperty(metaProperty);
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public String getStringValue() {
+ return stringValue;
+ }
+
+ public void testValidateMandatory() {
+ metaProperty.setName("stringValue");
+
+ // test not-null value that is mandatory
+ metaProperty.setMandatory(true);
+ stringValue = "some value";
+ validation.validateMandatory(context);
+ assertTrue(reasons.isEmpty());
+
+ // test null value that is mandatory
+ context.unknownValue();
+ stringValue = null;
+ validation.validateMandatory(context);
+ assertTrue(reasons.contains(Reasons.MANDATORY));
+
+ // test null value that is NOT mandatory
+ context.unknownValue();
+ metaProperty.setMandatory(false);
+ reasons.clear();
+ validation.validateMandatory(context);
+ assertTrue(reasons.isEmpty());
+ }
+
+ public void testValidateMaxLength() {
+ metaProperty.setName("stringValue");
+ metaProperty.putFeature(Features.Property.MAX_LENGTH, 5);
+ stringValue = "1234";
+ validation.validateMaxLength(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ stringValue = "much too long";
+ validation.validateMaxLength(context);
+ assertTrue(reasons.contains(Reasons.MAX_LENGTH));
+ }
+
+ public void testValidateMinLength() {
+ metaProperty.setName("stringValue");
+ metaProperty.putFeature(Features.Property.MIN_LENGTH, 5);
+ stringValue = "123456";
+ validation.validateMinLength(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ stringValue = "123";
+ validation.validateMinLength(context);
+ assertTrue(reasons.contains(Reasons.MIN_LENGTH));
+ }
+
+ public void testValidateMaxValue() {
+ metaProperty.setName("stringValue");
+ metaProperty.putFeature(Features.Property.MAX_VALUE, "9999");
+ stringValue = "1111";
+ validation.validateMaxValue(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ stringValue = "99999";
+ validation.validateMaxValue(context);
+ assertTrue(reasons.contains(Reasons.MAX_VALUE));
+ }
+
+ public void testValidateMinValue() {
+ metaProperty.setName("stringValue");
+ metaProperty.putFeature(Features.Property.MIN_VALUE, "5555");
+ stringValue = "8888";
+ validation.validateMinValue(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ stringValue = "3333";
+ validation.validateMinValue(context);
+ assertTrue(reasons.contains(Reasons.MIN_VALUE));
+ }
+
+ public int getIntValue() {
+ return intValue;
+ }
+
+ public void testValidateMinValue_MixedNumber() {
+ metaProperty.setName("intValue");
+ metaProperty.putFeature(Features.Property.MIN_VALUE, new Long(0));
+ intValue = 5;
+ validation.validateMinValue(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ intValue = -1;
+ validation.validateMinValue(context);
+ assertTrue(reasons.contains(Reasons.MIN_VALUE));
+ }
+
+ public void testValidateMinValue_Date_Timestamp() {
+ metaProperty.setName("dateValue");
+ Date dt = new Date();
+ metaProperty.putFeature(Features.Property.MIN_VALUE, dt);
+ dateValue = new Timestamp(dt.getTime()+1000);
+ validation.validateMinValue(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ dateValue = new Timestamp(dt.getTime()-1000);
+ validation.validateMinValue(context);
+ assertTrue(reasons.contains(Reasons.MIN_VALUE));
+ }
+
+ public void testValidateMaxValue_AlphabeticString() {
+ metaProperty.setName("stringValue");
+ metaProperty.putFeature(Features.Property.MAX_VALUE, "BBBB");
+ stringValue = "AAAA";
+ validation.validateMaxValue(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ stringValue = "BBBC";
+ validation.validateMaxValue(context);
+ assertTrue(reasons.contains(Reasons.MAX_VALUE));
+ }
+
+ public void testValidateRegExp() {
+ // regexp for Zip
+ String regexp = "[a-zA-Z\\- \\d]*";
+ metaProperty.setName("stringValue");
+ metaProperty.putFeature(Features.Property.REG_EXP, regexp);
+ stringValue = "53773";
+ validation.validateRegExp(context);
+ assertTrue(reasons.isEmpty());
+ context.unknownValue();
+ stringValue = "5355/7"; // invalid zip value
+ validation.validateRegExp(context);
+ assertTrue(reasons.contains(Reasons.REG_EXP));
+ }
+
+ public Date getDateValue() {
+ return dateValue;
+ }
+
+ public void testValidateTimeLag() {
+ metaProperty.setName("dateValue");
+ metaProperty.putFeature(Features.Property.TIME_LAG, XMLMetaValue.TIMELAG_Past);
+
+ dateValue = new Date(System.currentTimeMillis() - 10000);
+ validation.validateTimeLag(context);
+ assertTrue(reasons.isEmpty());
+
+ metaProperty.putFeature(Features.Property.TIME_LAG, XMLMetaValue.TIMELAG_Future);
+ validation.validateTimeLag(context);
+ assertTrue(reasons.contains(Reasons.TIME_LAG));
+
+ }
+
+ public static Test suite() {
+ return new TestSuite(StandardValidationTest.class);
+ }
+
+ public <T extends ValidationListener> void addError(String reason, ValidationContext<T> context) {
+ reasons.add(reason);
+ }
+
+ public <T extends ValidationListener> void addError(ValidationListener.Error error, ValidationContext<T> context) {
+ reasons.add(error.getReason());
+ }
+
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/xml/BeanValidatorTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/BeanValidatorTest.java
new file mode 100644
index 0000000..956da15
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/BeanValidatorTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.BeanValidator;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.ValidationResults;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.example.BusinessObjectAddress;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.routines.Reasons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * BeanValidator Tester.
+ */
+public class BeanValidatorTest extends TestCase {
+ public BeanValidatorTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testValidateMapAsBean() {
+ XMLMetaBeanManagerFactory.getRegistry().addLoader(new XMLMetaBeanURLLoader(
+ BusinessObject.class.getResource("test-beanInfos.xml")));
+
+ MetaBean mb = XMLMetaBeanManagerFactory.getFinder()
+ .findForId("org.apache.bval.example.Address");
+
+ // 1. validate a bean
+ BusinessObjectAddress adr = new BusinessObjectAddress();
+ BeanValidator<ValidationResults> validator = new BeanValidator<ValidationResults>();
+ ValidationResults results = validator.validate(adr, mb);
+ assertEquals(2,
+ results.getErrorsByReason().get(Features.Property.MANDATORY).size());
+
+ // 2. validate a map with the same metabean
+ validator.setTreatMapsLikeBeans(true);
+ results = validator.validate(new HashMap<String, Object>(), mb);
+ assertFalse(results.isEmpty());
+ assertEquals(2,
+ results.getErrorsByReason().get(Features.Property.MANDATORY).size());
+
+ // 3. validate as empty map (jsr303 behavior)
+ validator.setTreatMapsLikeBeans(false);
+ results = validator.validate(new HashMap<Object, Object>(), mb);
+ assertTrue(results.isEmpty());
+ }
+
+ public void testValidate() {
+ MetaBeanFinder finder = XMLMetaBeanManagerFactory.getFinder();
+ XMLMetaBeanManagerFactory.getRegistry().addLoader(new XMLMetaBeanURLLoader(
+ BusinessObject.class.getResource("test-beanInfos.xml")));
+ MetaBean info = finder.findForClass(BusinessObject.class);
+ BusinessObject object = new BusinessObject();
+ object.setAddress(new BusinessObjectAddress());
+ object.getAddress().setOwner(object);
+ BeanValidator<ValidationResults> validator = new BeanValidator<ValidationResults>();
+ ValidationResults results = validator.validate(object, info);
+ assertTrue(results.hasErrorForReason(Reasons.MANDATORY));
+ assertTrue(results.hasError(object, null));
+ assertTrue(results.hasError(object.getAddress(), null));
+
+ assertTrue(
+ validator.validateProperty(object, info.getProperty("firstName")).hasError(
+ object, "firstName"));
+
+ object.setUserId(1L);
+ object.setFirstName("Hans");
+ object.setLastName("Tester");
+ object.setAddress(new BusinessObjectAddress());
+ object.getAddress().setOwner(object);
+ assertFalse(validator.validate(object, info).isEmpty());
+
+ object.getAddress().setCountry("0123456789012345678");
+ assertFalse(validator.validate(object, info).isEmpty());
+
+ object.getAddress().setCountry("Germany");
+ object.setAddresses(new ArrayList<BusinessObjectAddress>());
+ object.getAddresses().add(object.getAddress());
+ object.getAddresses().add(object.getAddress());
+ object.getAddresses().add(object.getAddress());
+ assertTrue(validator.validate(object, info).isEmpty());
+
+ // 4th address is too much!
+ object.getAddresses().add(object.getAddress());
+ assertFalse(
+ validator.validate(object, info).isEmpty()); // cardinality error found
+ }
+
+ public static Test suite() {
+ return new TestSuite(BeanValidatorTest.class);
+ }
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanInfosTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanInfosTest.java
new file mode 100644
index 0000000..79fd3dc
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanInfosTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.example.BusinessObjectAddress;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * XMLMetaBean Tester.
+ */
+public class XMLMetaBeanInfosTest extends TestCase {
+
+ public XMLMetaBeanInfosTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testBeanInfosToXML() {
+ XMLMetaBeanInfos infos = new XMLMetaBeanInfos();
+ infos.setBeans(new ArrayList<XMLMetaBean>());
+ infos.setValidators(new ArrayList<XMLMetaValidator>());
+
+ XMLMetaValidator validator = new XMLMetaValidator();
+ validator.setId("mandatory");
+ validator.setJava("org.apache.bval.MandatoryValidator");
+
+ infos.getValidators().add(validator);
+
+ validator = new XMLMetaValidator();
+ validator.setId("email");
+ validator.setJava("org.apache.bval.EMailValidation");
+
+ infos.getValidators().add(validator);
+
+ XMLMetaBean bean = new XMLMetaBean();
+ bean.putFeature("DOMAIN", "TestProfile");
+ bean.putFeature("label-key", "business-object-label");
+ bean.setId("User");
+ bean.setImpl(BusinessObject.class.getName());
+ bean.setProperties(new ArrayList<XMLMetaProperty>());
+ XMLMetaProperty property = new XMLMetaProperty();
+ property.setName("userId");
+ property.setMandatory(XMLMetaValue.MANDATORY);
+ bean.getProperties().add(property);
+
+ property = new XMLMetaProperty();
+ property.setName("firstName");
+ property.setMandatory(XMLMetaValue.MANDATORY);
+ property.setMaxLength(100);
+ bean.getProperties().add(property);
+
+ property = new XMLMetaProperty();
+ property.setName("lastName");
+ property.setMandatory(XMLMetaValue.MANDATORY);
+ property.setMaxLength(100);
+ bean.getProperties().add(property);
+
+ property = new XMLMetaProperty();
+ property.setName("title");
+ property.setMandatory(XMLMetaValue.OPTIONAL);
+ property.setMaxLength(10);
+ bean.getProperties().add(property);
+
+ property = new XMLMetaProperty();
+ property.setName("dateBirth");
+ property.setMandatory(XMLMetaValue.OPTIONAL);
+ property.setTimeLag(XMLMetaValue.TIMELAG_Past);
+ bean.getProperties().add(property);
+
+ property = new XMLMetaProperty();
+ property.setName("validTo");
+ property.setMandatory(XMLMetaValue.OPTIONAL);
+ property.setTimeLag(XMLMetaValue.TIMELAG_Future);
+ bean.getProperties().add(property);
+
+ property = new XMLMetaProperty();
+ property.setName("email");
+ property.putFeature(XMLMetaValue.ANNOKEY_Widget, "entry");
+ property.putFeature(XMLMetaValue.ANNOKEY_TableColumn, true);
+ Map<String, String> formatterMap = new HashMap<String, String>();
+ formatterMap.put("locale", "DE");
+ formatterMap.put("style", "info");
+ property.putFeature("ajax-formatter", formatterMap);
+ property.addValidator("email");
+ bean.getProperties().add(property);
+
+ infos.getBeans().add(bean);
+
+ XMLMetaBean bean2 = new XMLMetaBean();
+ bean2.setId("Address");
+ bean2.setImpl(BusinessObjectAddress.class.getName());
+ property = new XMLMetaProperty();
+ property.setName("city");
+ bean2.putProperty(property);
+ property = new XMLMetaProperty();
+ property.setName("country");
+ property.setMaxLength(10);
+ property.setMandatory(XMLMetaValue.MANDATORY);
+ bean2.putProperty(property);
+
+ XMLMetaBeanReference relation = new XMLMetaBeanReference();
+ relation.setName("address");
+ relation.setBeanId("Address");
+ relation.setMandatory(XMLMetaValue.OPTIONAL);
+// relation.setDisplayName("UserAddress");
+ bean.putBeanRef(relation);
+
+ infos.getBeans().add(bean2);
+
+ String xml = XMLMapper.getInstance().getXStream().toXML(infos);
+// System.out.println(xml);
+ XMLMetaBeanInfos infos2 =
+ (XMLMetaBeanInfos) XMLMapper.getInstance().getXStream().fromXML(xml);
+ assertEquals(2, infos2.getBeans().size());
+ }
+
+
+ public void testMaxValueParsing() {
+ String xml = "\n" +
+ "<beanInfos> <bean id=\"org.apache.bval.test.model.Profile\">\n" +
+ " <property name=\"activationDay\" minValue=\"1\" maxValue=\"31\"/>\n" +
+ " <property name=\"activationMonth\" minValue=\"1\" maxValue=\"12\"/>\n" +
+ " </bean></beanInfos>";
+ XMLMetaBeanInfos beanInfos = (XMLMetaBeanInfos) XMLMapper.getInstance()
+ .getXStream().fromXML(xml);
+ Assert.assertNotNull(beanInfos);
+ assertEquals(Integer.valueOf(31),
+ beanInfos.getBeans().get(0).getProperty("activationDay").getMaxValue());
+ assertEquals(Integer.valueOf(1),
+ beanInfos.getBeans().get(0).getProperty("activationDay").getMinValue());
+ }
+
+ public static Test suite() {
+ return new TestSuite(XMLMetaBeanInfosTest.class);
+ }
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanManagerTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanManagerTest.java
new file mode 100644
index 0000000..a57c306
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanManagerTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.bval.xml;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.model.MetaBean;
+
+import java.util.Map;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:28:48<br>
+ */
+public class XMLMetaBeanManagerTest extends TestCase {
+ XMLMetaBeanManager mbm = new XMLMetaBeanManager();
+
+ public XMLMetaBeanManagerTest(String name) {
+ super(name);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mbm.addLoader(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos.xml")));
+ }
+
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testEnrichCopies() throws Exception {
+ Map<String, MetaBean> copies =
+ mbm.enrichCopies(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos-custom.xml"))
+ .load());
+ assertNotNull(copies);
+ MetaBean mb = copies.get(BusinessObject.class.getName());
+ assertFalse(mb.getProperty("lastName").isMandatory());
+ MetaBean mb2 = mbm.findForClass(BusinessObject.class);
+ assertTrue(mb2.getProperty("lastName").isMandatory());
+ }
+
+ public void testCopy() {
+ MetaBean mb = mbm.findForClass(BusinessObject.class);
+ MetaBean mb2 = mb.copy();
+ assertTrue(mb2 != mb);
+ assertTrue(mb2.getProperty("dateBirth") != mb.getProperty("dateBirth"));
+ }
+
+ @SuppressWarnings("deprecation")
+ public void testFindForClass() throws Exception {
+ MetaBeanFinder finder = mbm;
+ MetaBean info = finder.findForClass(BusinessObject.class);
+ assertNotNull(info);
+ assertTrue(info == info.getProperty("address").getMetaBean().getProperty("owner").getMetaBean());
+ assertTrue(info == info.getProperty("addresses").getMetaBean().getProperty("owner").getMetaBean());
+ assertTrue(info.getProperty("email").getJavaScriptValidations().length > 0);
+ }
+
+ public void testFindAll() {
+ Map<String, MetaBean> all = mbm.findAll();
+ assertNotNull(all);
+ Map<String, MetaBean> all2 = mbm.findAll();
+ assertEquals(all.size(), all2.size());
+ assertTrue(all.get(BusinessObject.class.getName()) == all2.get(BusinessObject.class.getName()));
+ assertTrue(all.get(BusinessObject.class.getName()) != null);
+ MetaBean bean = all.get(BusinessObject.class.getName());
+ assertTrue(bean == bean.getProperty("address").getMetaBean().getProperty("owner").getMetaBean());
+ assertTrue(bean == bean.getProperty("addresses").getMetaBean().getProperty("owner").getMetaBean());
+ }
+
+ public static Test suite() {
+ return new TestSuite(XMLMetaBeanManagerTest.class);
+ }
+}
diff --git a/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos-custom.xml b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos-custom.xml
new file mode 100644
index 0000000..d9f6b72
--- /dev/null
+++ b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos-custom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beanInfos>
+
+ <bean id="org.apache.bval.example.BusinessObject"
+ impl="org.apache.bval.example.BusinessObject">
+ <property name="lastName" mandatory="false"/>
+ <property name="unknownName" mandatory="true"/>
+ </bean>
+
+ <bean id="UnknownObject" impl="org.apache.bval.UnknownClass">
+ <property name="fullName" mandatory="true"/>
+ </bean>
+</beanInfos>
\ No newline at end of file
diff --git a/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos.xml b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos.xml
new file mode 100644
index 0000000..f78aa47
--- /dev/null
+++ b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<beanInfos>
+ <validator id="standard" java="org.apache.bval.routines.NOPValidation"
+ jsFunction="noop"/>
+ <validator id="email" java="org.apache.bval.routines.EMailValidation"/>
+ <bean id="org.apache.bval.example.BusinessObject"
+ impl="org.apache.bval.example.BusinessObject">
+ <feature key="mainKey">
+ <value class="string">userId</value>
+ </feature>
+ <feature key="DOMAIN">
+ <value class="string">TestObjects</value>
+ </feature>
+ <feature key="WIDGET">
+ <value class="string">entry</value>
+ </feature>
+ <property name="userId" mandatory="true"/>
+ <property name="firstName" mandatory="true" maxLength="100"/>
+ <property name="lastName" mandatory="true" maxLength="100"/>
+ <property name="title" mandatory="false" maxLength="10"/>
+ <property timeLag="PAST" name="dateBirth" mandatory="false"/>
+ <property timeLag="FUTURE" name="validTo" mandatory="false"/>
+ <property name="email">
+ <feature key="WIDGET">
+ <value class="string">entry</value>
+ </feature>
+ <feature key="TABLE_COLUMN">
+ <value class="boolean">true</value>
+ </feature>
+ <feature key="ajax-formatter">
+ <value class="map">
+ <entry>
+ <string>locale</string>
+ <string>DE</string>
+ </entry>
+ <entry>
+ <string>style</string>
+ <string>info</string>
+ </entry>
+ </value>
+ </feature>
+ <validator refId="email"/>
+ <validator refId="standard"/>
+ </property>
+ <relationship beanId="org.apache.bval.example.Address" name="address" mandatory="false"
+ displayName="UserAddress"/>
+ <relationship beanId="org.apache.bval.example.Address" name="addresses" maxLength="3"/>
+ </bean>
+ <bean id="org.apache.bval.example.Address"
+ impl="org.apache.bval.example.BusinessObjectAddress">
+ <feature key="DOMAIN">
+ <value class="string">TestObjects</value>
+ </feature>
+ <property name="city"/>
+ <property name="country" mandatory="true" maxLength="10"/>
+ <relationship name="owner" beanId="org.apache.bval.example.BusinessObject"
+ mandatory="true"/>
+ </bean>
+</beanInfos>
\ No newline at end of file
diff --git a/trunk/pom.xml b/trunk/pom.xml
new file mode 100644
index 0000000..3eba1b4
--- /dev/null
+++ b/trunk/pom.xml
@@ -0,0 +1,624 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+--><!--
+ Maven release plugin requires the project tag to be on a single line.
+--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>10</version>
+ <relativePath />
+ </parent>
+
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-parent</artifactId>
+ <name>Apache BVal :: bval-parent (Parent POM)</name>
+ <packaging>pom</packaging>
+ <version>0.4</version>
+
+ <description>Apache BVal parent pom</description>
+
+ <properties>
+ <site.deploy.url>scp://people.apache.org/home/${user.name}/public_html/bval/${project.version}/staging-site</site.deploy.url>
+ </properties>
+
+ <inceptionYear>2010</inceptionYear>
+
+ <url>http://bval.apache.org/</url>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/bval/tags/0.4</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/bval/tags/0.4</developerConnection>
+ <url>http://svn.apache.org/repos/asf/bval/tags/0.4</url>
+ </scm>
+
+ <mailingLists>
+ <mailingList>
+ <name>BVal User List</name>
+ <subscribe>user-subscribe@bval.apache.org</subscribe>
+ <unsubscribe>user-unsubscribe@bval.apache.org</unsubscribe>
+ <post>mailto:user@bval.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/bval-user/</archive>
+ </mailingList>
+
+ <mailingList>
+ <name>BVal Developer List</name>
+ <subscribe>dev-subscribe@bval.apache.org</subscribe>
+ <unsubscribe>dev-unsubscribe@bval.apache.org</unsubscribe>
+ <post>mailto:dev@bval.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/bval-dev/</archive>
+ </mailingList>
+
+ <mailingList>
+ <name>BVal Commits List</name>
+ <subscribe>commits-subscribe@bval.apache.org</subscribe>
+ <unsubscribe>commits-unsubscribe@bval.apache.org</unsubscribe>
+ <post>mailto:commits@bval.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/bval-commits/</archive>
+ </mailingList>
+ </mailingLists>
+
+ <issueManagement>
+ <url>https://issues.apache.org/jira/browse/BVAL</url>
+ </issueManagement>
+
+ <developers>
+ <developer>
+ <id>allee8285</id>
+ <name>Albert Lee</name>
+ <email>allee8285@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>carlosvara</id>
+ <name>Carlos Vara</name>
+ <email>carlosvara@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>djencks</id>
+ <name>David Jencks</name>
+ <email>djencks@apache.org</email>
+ <organization>IBM</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>dwoods</id>
+ <name>Donald Woods</name>
+ <email>dwoods@apache.org</email>
+ <organization>IBM</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>gpetracek</id>
+ <name>Gerhard Petracek</name>
+ <email>gpetracek@apache.org</email>
+ <organization>IRIAN Solutions GmbH</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>jrbauer</id>
+ <name>Jeremy Bauer</name>
+ <email>jrbauer@apache.org</email>
+ <organization>IBM</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>kevan</id>
+ <name>Kevan Miller</name>
+ <email>kevan@apache.org</email>
+ <organization>IBM</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>lresende</id>
+ <name>Luciano Resende</name>
+ <email>lresende@apache.org</email>
+ <organization>IBM</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>struberg</id>
+ <name>Mark Struberg</name>
+ <email>struberg@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>mbenson</id>
+ <name>Matt Benson</name>
+ <email>mbenson@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>matzew</id>
+ <name>Matthias Wessendorf</name>
+ <email>matzew@apache.org</email>
+ <organization>Kaazing</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>mnour</id>
+ <name>Mohammad Nour El-Din</name>
+ <email>mnour@apache.org</email>
+ <organization>Thebe Technology</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>romanstumm</id>
+ <name>Roman Stumm</name>
+ <email>romanstumm@apache.org</email>
+ <organization>Viaboxx GmbH</organization>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ <developer>
+ <id>simonetripodi</id>
+ <name>Simone Tripodi</name>
+ <email>simonetripodi@apache.org</email>
+ <roles>
+ <role>PMC Member</role>
+ </roles>
+ </developer>
+ </developers>
+
+ <distributionManagement>
+ <site>
+ <id>people.apache.org</id>
+ <url>${site.deploy.url}</url>
+ </site>
+ </distributionManagement>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>2.1.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <show>package</show>
+ <quiet>true</quiet>
+ <source>1.5</source>
+ <links>
+ <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+ <link>http://java.sun.com/javaee/6/docs/api</link>
+ </links>
+ </configuration>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>javadoc</report>
+ <report>test-javadoc</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jdepend-maven-plugin</artifactId>
+ <version>2.0-beta-2</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>2.3.2</version>
+ </plugin>
+ </plugins>
+ </reporting>
+
+ <!--
+ default versions for dependencies that child modules may include
+ -->
+ <dependencyManagement>
+ <dependencies>
+ <!-- Default of Apache Geronimo version of the Spec API -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-validation_1.0_spec</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <!-- Optional profile to use Spec RI API -->
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>1.0.0.GA</version>
+ </dependency>
+ <!-- JPA2 spec required for JPA TraversableResolver support -->
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-jpa_2.0_spec</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>1.8.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-beanutils</groupId>
+ <artifactId>commons-beanutils-core</artifactId>
+ <version>1.8.3</version>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>com.thoughtworks.xstream</groupId>
+ <artifactId>xstream</artifactId>
+ <version>1.3.1</version>
+ </dependency>
+ <!-- Optional - only used by bval-json -->
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>2.3.15</version>
+ </dependency>
+ <!-- Optional - only used by bval-guice -->
+ <dependency>
+ <groupId>javax.inject</groupId>
+ <artifactId>javax.inject</artifactId>
+ <version>1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ <version>3.0-rc2</version>
+ </dependency>
+ <dependency>
+ <groupId>aopalliance</groupId>
+ <artifactId>aopalliance</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <!-- Added mainly for testing -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <version>1.6.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.6.1</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ </testResource>
+ </testResources>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ <manifestEntries>
+ <Implementation-Build>${buildNumber}</Implementation-Build>
+ <Specification-Title>JSR-303 Bean Validation</Specification-Title>
+ <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
+ <Specification-Version>1.0</Specification-Version>
+ </manifestEntries>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <encoding>${project.build.sourceEncoding}</encoding>
+ <additionalparam>${javadoc.additionalparam}</additionalparam>
+ <aggregate>true</aggregate>
+ <subpackages>org.apache</subpackages>
+ <verbose>false</verbose>
+ <maxmemory>512m</maxmemory>
+ <links>
+ <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+ <link>http://java.sun.com/javaee/6/docs/api</link>
+ </links>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>process-remote-resources</id>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <resourceBundles>
+ <resourceBundle>org.apache:apache-jar-resource-bundle:1.4</resourceBundle>
+ </resourceBundles>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jdepend-maven-plugin</artifactId>
+ <version>2.0-beta-2</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.0.1</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Implementation-Title>${project.name}</Implementation-Title>
+ <Implementation-Version>${project.version}</Implementation-Version>
+ <Bundle-DocURL>${project.url}</Bundle-DocURL>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>ianal-maven-plugin</artifactId>
+ <version>1.0-alpha-1</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>verify-legal-files</goal>
+ </goals>
+ <configuration>
+ <!-- Fail the build if any artifacts are missing legal files -->
+ <strict>true</strict>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>0.7</version>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <excludeSubProjects>false</excludeSubProjects>
+ <numUnapprovedLicenses>0</numUnapprovedLicenses>
+ <excludes>
+ <!-- General informational files -->
+ <exclude>README.txt</exclude>
+ <exclude>CHANGES.txt</exclude>
+ <!-- generated content -->
+ <exclude>release.properties</exclude>
+ <exclude>DEPENDENCIES</exclude>
+ <exclude>**/.*/**</exclude>
+ <exclude>**/*.log</exclude>
+ <exclude>**/maven-eclipse.xml</exclude>
+ <exclude>**/rat.txt</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>**/*.iml</exclude>
+ <exclude>**/*.ipr</exclude>
+ <exclude>**/*.iws</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.2</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-velocity</artifactId>
+ <version>1.1.3</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <!-- inherited from apache-10.pom, but need config updates -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <configuration>
+ <useReleaseProfile>false</useReleaseProfile>
+ <goals>deploy site site-deploy</goals>
+ <arguments>-Papache-release</arguments>
+ <autoVersionSubmodules>true</autoVersionSubmodules>
+ <preparationGoals>clean install</preparationGoals>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.12</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>2.12</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>2.3.2</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>buildnumber-maven-plugin</artifactId>
+ <version>1.0-beta-4</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.1.1</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>validate</phase>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireMavenVersion>
+ <version>[2.2.1,)</version>
+ </requireMavenVersion>
+ <requireJavaVersion>
+ <version>[1.5,)</version>
+ </requireJavaVersion>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>0.7</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>ianal-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>buildnumber-maven-plugin</artifactId>
+ <configuration>
+ <getRevisionOnlyOnce>true</getRevisionOnlyOnce>
+ <revisionOnScmFailure>offline</revisionOnScmFailure>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>validate</phase>
+ <goals>
+ <goal>create</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-source</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Don't include in all builds for now
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ </plugin>
+ -->
+ </plugins>
+ </build>
+
+ <modules>
+ <module>bval-core</module>
+ <module>bval-xstream</module>
+ <module>bval-jsr303</module>
+ <module>bundle</module>
+ <module>bval-json</module>
+ <module>bval-guice</module>
+ <module>bval-tck</module>
+ <module>bval-extras</module>
+ </modules>
+
+</project>