incubator-bval-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mat...@apache.org
Subject svn commit: r924153 [4/13] - in /incubator/bval/trunk: ./ agimatec-jsr303/ agimatec-jsr303/src/ agimatec-jsr303/src/main/ agimatec-jsr303/src/main/java/ agimatec-jsr303/src/main/java/com/ agimatec-jsr303/src/main/java/com/agimatec/ agimatec-jsr303/src/...
Date Wed, 17 Mar 2010 04:39:14 GMT
Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidation.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidation.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidation.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidation.java Wed Mar 17 04:39:07 2010
@@ -0,0 +1,257 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.BeanValidationContext;
+import com.agimatec.validation.ValidationResults;
+import com.agimatec.validation.jsr303.util.NodeImpl;
+import com.agimatec.validation.jsr303.util.PathImpl;
+import com.agimatec.validation.model.Validation;
+import com.agimatec.validation.model.ValidationContext;
+import com.agimatec.validation.util.AccessStrategy;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+/**
+ * Description: Adapter between Constraint (JSR303) and Validation (Agimatec)<br/>
+ * this instance is immutable!<br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 17:31:36 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+public class ConstraintValidation<T extends Annotation>
+      implements Validation, ConstraintDescriptor<T> {
+    private static final String ANNOTATION_MESSAGE = "message";
+    private final ConstraintValidator validator;
+    private final 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;
+
+    /**
+     * @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
+     */
+    public ConstraintValidation(
+          Class<? extends ConstraintValidator<T, ?>>[] validatorClasses,
+          ConstraintValidator validator, T annotation, Class owner,
+          AccessStrategy access, boolean reportFromComposite) {
+        this.attributes = new HashMap();
+        this.validatorClasses = validatorClasses;
+        this.validator = validator;
+        this.annotation = annotation;
+        this.owner = owner;
+        this.access = access;
+        this.reportFromComposite = reportFromComposite;
+    }
+
+    void setGroups(Set<Class<?>> groups) {
+        this.groups = groups;
+    }
+
+    void setPayload(Set<Class<? extends Payload>> payload) {
+        this.payload = payload;
+    }
+
+    public boolean isReportAsSingleViolation() {
+        return reportFromComposite;
+    }
+
+    public void addComposed(ConstraintValidation aConstraintValidation) {
+        if (composedConstraints == null) {
+            composedConstraints = new HashSet();
+        }
+        composedConstraints.add(aConstraintValidation);
+    }
+
+    public void validate(ValidationContext context) {
+        validate((GroupValidationContext) context);
+    }
+
+    public void validate(GroupValidationContext context) {
+        context.setConstraintDescriptor(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 (validator != null && !context.collectValidated(context.getBean(), validator))
+            return; // already done
+
+        if (context.getMetaProperty() != null && !isCascadeEnabled(context)) {
+            return;
+        }
+
+        // process composed constraints
+        if (isReportAsSingleViolation()) {
+            BeanValidationContext gctx = (BeanValidationContext) context;
+            ConstraintValidationListener oldListener =
+                  ((ConstraintValidationListener) gctx.getListener());
+            ConstraintValidationListener listener =
+                  new ConstraintValidationListener(oldListener.getRootBean());
+            gctx.setListener(listener);
+            try {
+                for (ConstraintValidation composed : getComposingValidations()) {
+                    composed.validate(context);
+                }
+            } finally {
+                gctx.setListener(oldListener);
+            }
+            // stop validating when already failed and ReportAsSingleInvalidConstraint = true ?
+            if (!listener.getConstaintViolations().isEmpty()) {
+                // 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);
+            }
+        }
+
+        if (validator != null) {
+            ConstraintValidatorContextImpl jsrContext =
+                  new ConstraintValidatorContextImpl(context, this);
+            if (!validator.isValid(context.getValidatedValue(), jsrContext)) {
+                addErrors(context, jsrContext);
+            }
+        }
+    }
+
+    private boolean isCascadeEnabled(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);
+        }
+
+        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;
+    }
+
+    private void addErrors(GroupValidationContext context,
+                           ConstraintValidatorContextImpl jsrContext) {
+        for (ValidationResults.Error each : jsrContext.getErrorMessages()) {
+            context.getListener().addError(each, context);
+        }
+    }
+
+    public String toString() {
+        return "ConstraintValidation{" + validator + '}';
+    }
+
+    public String getMessageTemplate() {
+        return (String) attributes.get(ANNOTATION_MESSAGE);
+    }
+
+    public ConstraintValidator getValidator() {
+        return validator;
+    }
+
+    protected boolean isMemberOf(Class<?> reqGroup) {
+        /**
+         * owner: implicit grouping support:
+         * owner is reqGroup or a superclass/superinterface of reqGroup
+         */
+        return owner.isAssignableFrom(reqGroup) || groups.contains(reqGroup);
+    }
+
+    public Class getOwner() {
+        return owner;
+    }
+
+    public T getAnnotation() {
+        return annotation;
+    }
+
+    public AccessStrategy getAccess() {
+        return access;
+    }
+
+    /////////////////////////// ConstraintDescriptor implementation
+
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+        return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+    }
+
+    Set<ConstraintValidation<?>> getComposingValidations() {
+        return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+    }
+
+    public Set<Class<?>> getGroups() {
+        return groups;
+    }
+
+    public Set<Class<? extends Payload>> getPayload() {
+        return payload;
+    }
+
+    public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
+        return Arrays.asList(validatorClasses);
+    }
+
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidationListener.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidationListener.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidationListener.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidationListener.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.jsr303.util.PathImpl;
+import com.agimatec.validation.model.ValidationContext;
+import com.agimatec.validation.model.ValidationListener;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: <br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 14:52:19 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+public final class ConstraintValidationListener<T> implements ValidationListener {
+    private final Set<ConstraintViolation<T>> constaintViolations = new HashSet();
+    private final T rootBean;
+
+    public ConstraintValidationListener(T aRootBean) {
+        this.rootBean = aRootBean;
+    }
+
+    @SuppressWarnings({"ManualArrayToCollectionCopy"})
+    public void addError(String reason, ValidationContext context) {
+        addError(reason, null, context);
+    }
+
+    public void addError(Error error, ValidationContext 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) {
+        final Object value;
+
+        final ConstraintDescriptor constraint;
+        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);
+            }
+            constraint = gcontext.getConstraintDescriptor();
+            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());
+            constraint = null;
+        }
+        ConstraintViolationImpl<T> ic = new ConstraintViolationImpl<T>(messageTemplate,
+              message, rootBean, context.getBean(), propPath, value, constraint);
+        constaintViolations.add(ic);
+    }
+
+    public Set<ConstraintViolation<T>> getConstaintViolations() {
+        return constaintViolations;
+    }
+
+    public boolean isEmpty() {
+        return constaintViolations.isEmpty();
+    }
+
+    public T getRootBean() {
+        return rootBean;
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidatorContextImpl.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidatorContextImpl.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidatorContextImpl.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintValidatorContextImpl.java Wed Mar 17 04:39:07 2010
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.jsr303.util.NodeBuilderDefinedContextImpl;
+import com.agimatec.validation.jsr303.util.NodeImpl;
+import com.agimatec.validation.jsr303.util.PathImpl;
+import com.agimatec.validation.model.ValidationListener;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Path;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ * User: roman <br/>
+ * Date: 28.09.2009 <br/>
+ * Time: 14:19:30 <br/>
+ * Copyright: Agimatec GmbH
+ */
+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;
+
+    public ConstraintValidatorContextImpl(GroupValidationContext validationContext,
+                                          ConstraintValidation aConstraintValidation) {
+        this.validationContext = validationContext;
+        this.constraintDescriptor = aConstraintValidation;
+    }
+
+    public void disableDefaultConstraintViolation() {
+        defaultDisabled = true;
+    }
+
+    public String getDefaultConstraintMessageTemplate() {
+        return constraintDescriptor.getMessageTemplate();
+    }
+
+    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;
+
+        ConstraintViolationBuilderImpl(ConstraintValidatorContextImpl contextImpl,
+                                       String template, PathImpl path) {
+            parent = contextImpl;
+            messageTemplate = template;
+            propertyPath = path;
+        }
+
+        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);
+        }
+
+        public ConstraintValidatorContext addConstraintViolation() {
+            parent.addError(messageTemplate, propertyPath);
+            return parent;
+        }
+    }
+
+    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;
+    }
+
+    public ConstraintDescriptor<?> getConstraintDescriptor() {
+        return constraintDescriptor;
+    }
+
+    public GroupValidationContext getValidationContext() {
+        return validationContext;
+    }
+
+    public void addError(String messageTemplate, Path propertyPath) {
+        errorMessages.add(new ValidationListener.Error(messageTemplate, propertyPath, null));
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintViolationImpl.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintViolationImpl.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintViolationImpl.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ConstraintViolationImpl.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+
+/**
+ * Description: Describe a constraint validation defect<br/>
+ * From rootBean and propertyPath, it is possible to rebuild the context of the failure
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 14:50:12 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+class ConstraintViolationImpl<T> implements ConstraintViolation<T> {
+    private final String messageTemplate;
+    private final String message;
+    /** root bean validation was invoked on. */
+    private final T rootBean;
+    /** last bean validated. */
+    private final Object leafBean;
+    private final Object value;
+    private final Path propertyPath;
+    private final ConstraintDescriptor constraintDescriptor;
+
+    /**
+     * @param messageTemplate - message reason (raw message) 
+     * @param message - interpolated message (locale specific)
+     * @param rootBean
+     * @param leafBean
+     * @param propertyPath
+     * @param value
+     * @param constraintDescriptor
+     */
+    public ConstraintViolationImpl(String messageTemplate, String message, T rootBean, Object leafBean,
+                                   Path propertyPath, Object value,
+                                   ConstraintDescriptor constraintDescriptor) {
+        this.messageTemplate = messageTemplate;
+        this.message = message;
+        this.rootBean = rootBean;
+        this.propertyPath = propertyPath;
+        this.leafBean = leafBean;
+        this.value = value;
+        this.constraintDescriptor = constraintDescriptor;
+    }
+
+    /**
+     * former name getInterpolatedMessage()
+     * @return The interpolated error message for this constraint violation.
+     **/
+    public String getMessage() {
+        return message;
+    }
+
+    public String getMessageTemplate() {
+        return messageTemplate;
+    }
+
+    /** Root bean being validated validated */
+    public T getRootBean() {
+        return rootBean;
+    }
+
+    public Class getRootBeanClass() {
+        return rootBean == null ? null : rootBean.getClass();
+    }
+
+    public Object getLeafBean() {
+        return leafBean;
+    }
+
+    /** The value failing to pass the constraint */
+    public Object getInvalidValue() {
+        return value;
+    }
+
+    /**
+     * the property path to the value from <code>rootBean</code>
+     * Null if the value is the rootBean itself
+     */
+    public Path getPropertyPath() {
+        return propertyPath;
+    }
+
+    public ConstraintDescriptor getConstraintDescriptor() {
+        return constraintDescriptor;
+    }
+
+    public String toString() {
+        return "ConstraintViolationImpl{" + "rootBean=" + rootBean + ", propertyPath='" +
+              propertyPath + '\'' + ", message='" + message + '\'' + ", leafBean=" +
+              leafBean + ", value=" + value + '}';
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultConstraintValidatorFactory.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultConstraintValidatorFactory.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultConstraintValidatorFactory.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultConstraintValidatorFactory.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.jsr303.util.SecureActions;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+
+/**
+ * Description: create constraint instances with the default / no-arg constructor <br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 13:18:36 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+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(Class<T> constraintClass) {
+        return SecureActions.newInstance(constraintClass);
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultMessageInterpolator.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultMessageInterpolator.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultMessageInterpolator.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultMessageInterpolator.java Wed Mar 17 04:39:07 2010
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.agimatec.validation.jsr303;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.validation.MessageInterpolator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 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/>
+ * User: roman.stumm <br/>
+ * Date: 02.04.2008 <br/>
+ * Time: 17:21:51 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+public class DefaultMessageInterpolator implements MessageInterpolator {
+    private static final Log log = LogFactory.getLog(DefaultMessageInterpolator.class);
+    private static final String DEFAULT_VALIDATION_MESSAGES =
+          "com.agimatec.validation.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 there locale. */
+    private final Map<Locale, ResourceBundle> defaultBundlesMap =
+          new ConcurrentHashMap<Locale, ResourceBundle>();
+
+    public DefaultMessageInterpolator() {
+        this(null);
+    }
+
+    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 comparaison has to be based on == and not equals though
+        return interpolateMessage(message,
+              context.getConstraintDescriptor().getAttributes(), defaultLocale);
+    }
+
+    /** {@inheritDoc} */
+    public String interpolate(String message, Context context, Locale locale) {
+        return interpolateMessage(message,
+              context.getConstraintDescriptor().getAttributes(), locale);
+    }
+
+    /**
+     * Runs the message interpolation according to alogrithm specified in JSR 303.
+     * <br/>
+     * Note:
+     * <br/>
+     * Lookups in user bundles is 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);
+        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;
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader != null) {
+            rb = loadBundle(classLoader, locale,
+                  USER_VALIDATION_MESSAGES + " not found by thread local classloader");
+        }
+        if (rb == null) {
+            rb = loadBundle(this.getClass().getClassLoader(), locale,
+                  USER_VALIDATION_MESSAGES + " not found by validator classloader");
+        }
+        if (log.isDebugEnabled()) {
+            if (rb != null) {
+                log.debug(USER_VALIDATION_MESSAGES + " found");
+            } else {
+                log.debug(USER_VALIDATION_MESSAGES + " not found. Delegating to " +
+                      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.trace(message);
+        }
+        return rb;
+    }
+
+    private String replaceVariables(String message, ResourceBundle bundle, Locale locale,
+                                    boolean recurse) {
+        Matcher matcher = messageParameterPattern.matcher(message);
+        StringBuffer sb = new StringBuffer();
+        String resolvedParameterValue;
+        while (matcher.find()) {
+            String parameter = matcher.group(1);
+            resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse);
+
+            matcher.appendReplacement(sb, 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();
+        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 = message;
+            }
+            matcher.appendReplacement(sb, 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) {
+        if (defaultBundlesMap.containsKey(locale)) {
+            return defaultBundlesMap.get(locale);
+        }
+
+        ResourceBundle bundle =
+              ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, locale);
+        defaultBundlesMap.put(locale, bundle);
+        return bundle;
+    }
+
+    private ResourceBundle findUserResourceBundle(Locale locale) {
+        if (userBundlesMap.containsKey(locale)) {
+            return userBundlesMap.get(locale);
+        }
+
+        ResourceBundle bundle = getFileBasedResourceBundle(locale);
+        if (bundle != null) {
+            userBundlesMap.put(locale, bundle);
+        }
+        return bundle;
+    }
+
+    public void setLocale(Locale locale) {
+        defaultLocale = locale;
+    }
+
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultValidationProviderResolver.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultValidationProviderResolver.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultValidationProviderResolver.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/DefaultValidationProviderResolver.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.jsr303.util.SecureActions;
+
+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.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";
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * javax.validation.ValidationProviderResolver#getValidationProviders()
+     */
+    public List<ValidationProvider<?>> getValidationProviders() {
+        List<ValidationProvider<?>> providers = new ArrayList();
+        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
+                                providers.add((ValidationProvider) SecureActions.newInstance(provider));
+                            } 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;
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ElementDescriptorImpl.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ElementDescriptorImpl.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ElementDescriptorImpl.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/ElementDescriptorImpl.java Wed Mar 17 04:39:07 2010
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.model.MetaBean;
+import com.agimatec.validation.model.Validation;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: MetaData class<br/>
+ * User: roman.stumm <br/>
+ * Date: 02.04.2008 <br/>
+ * Time: 12:23:45 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+public abstract class ElementDescriptorImpl implements ElementDescriptor {
+    protected final MetaBean metaBean;
+    protected final Class elementClass;
+    private Set<ConstraintDescriptor<?>> constraintDescriptors;
+
+    protected ElementDescriptorImpl(MetaBean metaBean,
+                                 Validation[] validations) {
+        this.metaBean = metaBean;
+        this.elementClass = metaBean.getBeanClass();
+        createConstraintDescriptors(validations);
+    }
+
+    protected ElementDescriptorImpl(Class elementClass, Validation[] validations) {
+        this.metaBean = null;
+        this.elementClass = elementClass;
+        createConstraintDescriptors(validations);
+    }
+
+    /** @return Statically defined returned type. */
+    public Class getElementClass() {
+        return elementClass;
+    }
+
+    public ElementDescriptor.ConstraintFinder findConstraints() {
+        return new ConstraintFinderImpl(metaBean, constraintDescriptors);
+    }
+
+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return constraintDescriptors;
+    }
+
+    /** return true if at least one constraint declaration is present on the element. */
+    public boolean hasConstraints() {
+        return !constraintDescriptors.isEmpty();
+    }
+
+    private void createConstraintDescriptors(Validation[] validations) {
+        final Set<ConstraintDescriptor<?>> cds = new HashSet(validations.length);
+        for (Validation validation : validations) {
+            if (validation instanceof ConstraintValidation) {
+                ConstraintValidation cval = (ConstraintValidation) validation;
+                cds.add(cval);
+            }
+        }
+        setConstraintDescriptors(cds);
+    }
+
+    public void setConstraintDescriptors(Set<ConstraintDescriptor<?>> constraintDescriptors) {
+        this.constraintDescriptors = constraintDescriptors;
+    }
+
+    public MetaBean getMetaBean() {
+        return metaBean;
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContext.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContext.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContext.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContext.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.jsr303.groups.Group;
+import com.agimatec.validation.jsr303.groups.Groups;
+import com.agimatec.validation.jsr303.util.PathImpl;
+import com.agimatec.validation.model.MetaBean;
+import com.agimatec.validation.model.ValidationContext;
+import com.agimatec.validation.model.ValidationListener;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+
+/**
+ * Description: <br/>
+ * User: roman.stumm <br/>
+ * Date: 28.04.2008 <br/>
+ * Time: 10:15:08 <br/>
+ * Copyright: Agimatec GmbH
+ */
+public interface GroupValidationContext<T extends ValidationListener>
+      extends ValidationContext<T> {
+    /** the groups in their sequence for validation */
+    Groups getGroups();
+
+    void setCurrentGroup(Group group);
+
+    Group getCurrentGroup();
+
+    PathImpl getPropertyPath();
+
+    MetaBean getRootMetaBean();
+
+    void setConstraintDescriptor(ConstraintValidation constraint);
+
+    public ConstraintValidation getConstraintDescriptor();
+
+    public Object getValidatedValue();
+
+    void setFixedValue(Object value);
+
+    MessageInterpolator getMessageResolver();
+
+    TraversableResolver getTraversableResolver();
+
+    boolean collectValidated(Object bean, ConstraintValidator constraint);
+
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContextImpl.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContextImpl.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContextImpl.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/GroupValidationContextImpl.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.BeanValidationContext;
+import com.agimatec.validation.jsr303.groups.Group;
+import com.agimatec.validation.jsr303.groups.Groups;
+import com.agimatec.validation.jsr303.resolver.CachingTraversableResolver;
+import com.agimatec.validation.jsr303.util.NodeImpl;
+import com.agimatec.validation.jsr303.util.PathImpl;
+import com.agimatec.validation.model.MetaBean;
+import com.agimatec.validation.model.MetaProperty;
+import com.agimatec.validation.model.ValidationListener;
+import com.agimatec.validation.util.AccessStrategy;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Set;
+
+/**
+ * Description: instance per validation process, not thread-safe<br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 16:32:35 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+final class GroupValidationContextImpl<T extends ValidationListener>
+      extends BeanValidationContext<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;
+
+    /**
+     * contains the validation constraints that have already been processed during
+     * this validation routine (as part of a previous group match)
+     */
+    private IdentityHashMap<Object, IdentityHashMap<ConstraintValidator, Object>> validatedConstraints =
+          new IdentityHashMap();
+    private ConstraintValidation currentConstraint;
+    private final TraversableResolver traversableResolver;
+
+    public GroupValidationContextImpl(T listener, MessageInterpolator aMessageResolver,
+                                      TraversableResolver traversableResolver,
+                                      MetaBean rootMetaBean) {
+        super(listener);
+        this.messageResolver = aMessageResolver;
+        this.traversableResolver = CachingTraversableResolver.cacheFor(traversableResolver);
+        this.rootMetaBean = rootMetaBean;
+        this.path = PathImpl.create(null);
+    }
+
+    @Override
+    public void setCurrentIndex(int index) {
+        path.getLeafNode().setIndex(index);
+    }
+
+    @Override
+    public void setCurrentKey(Object key) {
+        path.getLeafNode().setKey(key);
+    }
+
+    @Override
+    public void moveDown(MetaProperty prop, AccessStrategy access) {
+        path.addNode(new NodeImpl(prop.getName()));
+        super.moveDown(prop, access);
+    }
+
+    @Override
+    public void moveUp(Object bean, MetaBean metaBean) {
+        path.removeLeafNode();
+        super.moveUp(bean, metaBean); // call super!
+    }
+
+    /**
+     * add the object in the current group
+     * to the collection of validated objects to keep
+     * track of them to avoid endless loops during validation.
+     *
+     * @return true when the object was not already validated in this context
+     */
+    @Override
+    public boolean collectValidated() {
+        Set<Group> groupSet = (Set<Group>) validatedObjects.get(getBean());
+        if (groupSet == null) {
+            groupSet = new HashSet(10);
+            validatedObjects.put(getBean(), groupSet);
+        }
+        return groupSet.add(getCurrentGroup());
+    }
+
+    /** @return true when the constraint for this object was not already validated in this context */
+    public boolean collectValidated(Object bean, ConstraintValidator constraint) {
+        IdentityHashMap<ConstraintValidator, Object> beanConstraints =
+              validatedConstraints.get(bean);
+        if (beanConstraints == null) {
+            beanConstraints = new IdentityHashMap();
+            validatedConstraints.put(bean, beanConstraints);
+        }
+        return beanConstraints.put(constraint, Boolean.TRUE) == null;
+    }
+
+    public boolean isValidated(Object bean, ConstraintValidator constraint) {
+        IdentityHashMap<ConstraintValidator, Object> beanConstraints =
+              validatedConstraints.get(bean);
+        return beanConstraints != null && beanConstraints.containsKey(constraint);
+    }
+
+    public void resetValidatedConstraints() {
+        validatedConstraints.clear();
+    }
+
+    /**
+     * 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.addNode(new NodeImpl(getMetaProperty().getName()));
+        }
+        return currentPath;
+    }
+
+    public MetaBean getRootMetaBean() {
+        return rootMetaBean;
+    }
+
+    public void setGroups(Groups groups) {
+        this.groups = groups;
+    }
+
+    public Groups getGroups() {
+        return groups;
+    }
+
+    public Group getCurrentGroup() {
+        return currentGroup;
+    }
+
+    public void setCurrentGroup(Group currentGroup) {
+        this.currentGroup = currentGroup;
+    }
+
+    public void setConstraintDescriptor(ConstraintValidation constraint) {
+        currentConstraint = constraint;
+    }
+
+    public ConstraintValidation getConstraintDescriptor() {
+        return currentConstraint;
+    }
+
+    /** @return value being validated */
+    public Object getValidatedValue() {
+        if (getMetaProperty() != null) {
+            return getPropertyValue(currentConstraint.getAccess());
+        } else {
+            return getBean();
+        }
+    }
+
+    public MessageInterpolator getMessageResolver() {
+        return messageResolver;
+    }
+
+    public TraversableResolver getTraversableResolver() {
+        return traversableResolver;
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303Features.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303Features.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303Features.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303Features.java Wed Mar 17 04:39:07 2010
@@ -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 com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.model.Features;
+
+/**
+ * Description: Contains MetaBean feature keys of additional features used in the implementation
+ * of JSR303<br/>
+ * User: roman.stumm <br/>
+ * Date: 02.04.2008 <br/>
+ * Time: 15:22:49 <br/>
+ * Copyright: Agimatec GmbH 2008
+ *
+ * @see com.agimatec.validation.model.FeaturesCapable
+ * @see com.agimatec.validation.model.Features
+ */
+public interface Jsr303Features {
+    interface Property extends Features.Property {
+        /** INFO: cached PropertyDescriptorImpl of the property */
+        String PropertyDescriptor = "PropertyDescriptor";
+    }
+
+    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 sortied Array with ValidationEntries */
+        String VALIDATION_SEQUENCE = "ValidationSequence";
+
+        /**
+         * INFO: cached BeanDescriptorImpl of the bean
+         */
+        String BEAN_DESCRIPTOR = "BeanDescriptor";
+    }
+}

Added: incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303MetaBeanFactory.java
URL: http://svn.apache.org/viewvc/incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303MetaBeanFactory.java?rev=924153&view=auto
==============================================================================
--- incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303MetaBeanFactory.java (added)
+++ incubator/bval/trunk/agimatec-jsr303/src/main/java/com/agimatec/validation/jsr303/Jsr303MetaBeanFactory.java Wed Mar 17 04:39:07 2010
@@ -0,0 +1,489 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.    
+ */
+package com.agimatec.validation.jsr303;
+
+import com.agimatec.validation.MetaBeanFactory;
+import com.agimatec.validation.jsr303.groups.Group;
+import com.agimatec.validation.jsr303.util.SecureActions;
+import com.agimatec.validation.jsr303.util.TypeUtils;
+import com.agimatec.validation.jsr303.xml.MetaConstraint;
+import com.agimatec.validation.model.Features;
+import com.agimatec.validation.model.MetaBean;
+import com.agimatec.validation.model.MetaProperty;
+import com.agimatec.validation.util.AccessStrategy;
+import com.agimatec.validation.util.FieldAccess;
+import com.agimatec.validation.util.MethodAccess;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.ClassUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.validation.*;
+import javax.validation.groups.Default;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 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/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 14:12:51 <br/>
+ * Copyright: Agimatec GmbH 2008
+ */
+public class Jsr303MetaBeanFactory implements MetaBeanFactory {
+    protected static final Log log = LogFactory.getLog(Jsr303MetaBeanFactory.class);
+    protected static final String ANNOTATION_VALUE = "value";
+    protected final AgimatecFactoryContext factoryContext;
+
+    public Jsr303MetaBeanFactory(AgimatecFactoryContext factoryContext) {
+        this.factoryContext = factoryContext;
+    }
+
+    private ConstraintValidatorFactory getConstraintValidatorFactory() {
+        return factoryContext.getConstraintValidatorFactory();
+    }
+
+    private ConstraintDefaults getDefaultConstraints() {
+        return factoryContext.getFactory().getDefaultConstraints();
+
+    }
+
+    /**
+     * 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);
+            for (Class interfaceClass : beanClass.getInterfaces()) {
+                processClass(interfaceClass, metabean);
+            }
+
+            // process class, superclasses and interfaces
+            List<Class> classSequence = new ArrayList<Class>();
+            Class theClass = beanClass;
+            while (theClass != null && theClass != Object.class) {
+                classSequence.add(theClass);
+                theClass = theClass.getSuperclass();
+            }
+            // 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);
+            }
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        } catch (InvocationTargetException e) {
+            throw new IllegalArgumentException(e.getTargetException());
+        }
+    }
+
+    /**
+     * process class annotations, field and method annotations
+     *
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    private void processClass(Class<?> beanClass, MetaBean metabean)
+          throws IllegalAccessException, InvocationTargetException {
+        if (!factoryContext.getFactory().getAnnotationIgnores()
+              .isIgnoreAnnotations(beanClass)) { // ignore on class level
+
+            processAnnotations(null, beanClass, beanClass, null,
+                  new AppendValidationToMeta(metabean));
+
+            Field[] fields = beanClass.getDeclaredFields();
+            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)) {
+                    if (metaProperty == null) {
+                        metaProperty = createMetaProperty(field.getName(), field.getType());
+                        /*if (*/
+                        processAnnotations(metaProperty, beanClass, field,
+                              new FieldAccess(field),
+                              new AppendValidationToMeta(metaProperty));//) {
+                        metabean.putProperty(metaProperty.getName(), metaProperty);
+                        //}
+                    } else {
+                        processAnnotations(metaProperty, beanClass, field,
+                              new FieldAccess(field),
+                              new AppendValidationToMeta(metaProperty));
+                    }
+                }
+            }
+            Method[] methods = beanClass.getDeclaredMethods();
+            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)) {
+                        MetaProperty metaProperty = metabean.getProperty(propName);
+                        // create a property for those methods for which there is not yet a MetaProperty
+                        if (metaProperty == null) {
+                            metaProperty =
+                                  createMetaProperty(propName, method.getReturnType());
+                            /*if (*/
+                            processAnnotations(metaProperty, beanClass, method,
+                                  new MethodAccess(propName, method),
+                                  new AppendValidationToMeta(metaProperty));//) {
+                            metabean.putProperty(propName, metaProperty);
+                            //}
+                        } else {
+                            processAnnotations(metaProperty, beanClass, method,
+                                  new MethodAccess(propName, method),
+                                  new AppendValidationToMeta(metaProperty));
+                        }
+                    }
+                }
+            }
+        }
+        addXmlConstraints(beanClass, metabean);
+    }
+
+    /** add cascade validation and constraints from xml mappings */
+    private void addXmlConstraints(Class<?> beanClass, MetaBean metabean)
+          throws IllegalAccessException, InvocationTargetException {
+        for (MetaConstraint<?, ? extends Annotation> meta : factoryContext.getFactory()
+              .getMetaConstraints(beanClass)) {
+            MetaProperty metaProperty;
+            if (meta.getAccessStrategy() == null) { // class level
+                metaProperty = null;
+            } else { // property level
+                metaProperty =
+                      metabean.getProperty(meta.getAccessStrategy().getPropertyName());
+                if (metaProperty == null) {
+                    metaProperty = createMetaProperty(
+                          meta.getAccessStrategy().getPropertyName(),
+                          meta.getAccessStrategy().getJavaType());
+                    metabean.putProperty(metaProperty.getName(), metaProperty);
+                }
+            }
+            Class<? extends ConstraintValidator<?, ?>>[] constraintClasses =
+                  findConstraintValidatorClasses(meta.getAnnotation(), null);
+            applyConstraint(meta.getAnnotation(), constraintClasses, metaProperty, beanClass,
+                  meta.getAccessStrategy(), new AppendValidationToMeta(
+                  metaProperty == null ? metabean : metaProperty));
+        }
+        for (AccessStrategy access : factoryContext.getFactory().getValidAccesses(beanClass)) {
+            MetaProperty metaProperty = metabean.getProperty(access.getPropertyName());
+            if (metaProperty == null) {
+                metaProperty =
+                      createMetaProperty(access.getPropertyName(), access.getJavaType());
+                metabean.putProperty(metaProperty.getName(), metaProperty);
+            }
+            processValid(metaProperty, access);
+        }
+    }
+
+    private MetaProperty createMetaProperty(String propName, Type type) {
+        MetaProperty metaProperty;
+        metaProperty = new MetaProperty();
+        metaProperty.setName(propName);
+        metaProperty.setType(type);
+        return metaProperty;
+    }
+
+    private 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;
+    }
+
+    private boolean processAnnotation(Annotation annotation, MetaProperty prop, Class owner,
+                                      AccessStrategy access, AppendValidation appender)
+          throws IllegalAccessException, InvocationTargetException {
+        if (annotation instanceof Valid) {
+            return processValid(prop, access);
+        } else {
+            /**
+             * 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) {
+                Class<? extends ConstraintValidator<?, ?>>[] validatorClasses;
+                validatorClasses = findConstraintValidatorClasses(annotation, vcAnno);
+                return applyConstraint(annotation, validatorClasses, prop, owner, access,
+                      appender);
+            } else {
+                /**
+                 * 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, ANNOTATION_VALUE);
+                if (result != null && result instanceof Annotation[]) {
+                    boolean changed = false;
+                    for (Annotation each : (Annotation[]) result) {
+                        changed |= processAnnotation(each, prop, owner, access, appender);
+                    }
+                    return changed;
+                }
+            }
+        }
+        return false;
+    }
+
+    protected Class<? extends ConstraintValidator<?, ?>>[] findConstraintValidatorClasses(
+          Annotation annotation, Constraint vcAnno) {
+        if (vcAnno == null) {
+            vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+        }
+        Class<? extends ConstraintValidator<?, ?>>[] validatorClasses;
+        validatorClasses = factoryContext.getFactory()
+              .getConstraintsCache()
+              .getConstraintValidators(annotation.annotationType());
+        if (validatorClasses == null) {
+            validatorClasses = vcAnno.validatedBy();
+            if (validatorClasses.length == 0) {
+                validatorClasses = getDefaultConstraints()
+                      .getValidatorClasses(annotation.annotationType());
+            }
+        }
+        return validatorClasses;
+    }
+
+    private boolean processValid(MetaProperty prop, AccessStrategy access) {
+        if (prop != null/* && prop.getMetaBean() == null*/) {
+            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)) {
+                    AccessStrategy[] strategies_new =
+                          new AccessStrategy[strategies.length + 1];
+                    System.arraycopy(strategies, 0, strategies_new, 0, strategies.length);
+                    strategies_new[strategies.length] = access;
+                    prop.putFeature(Features.Property.REF_CASCADE, strategies_new);
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void processGroupSequence(Class<?> beanClass, MetaBean metabean) {
+        GroupSequence annotation = beanClass.getAnnotation(GroupSequence.class);
+        List<Group> groupSeq = metabean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+        if (groupSeq == null) {
+            groupSeq = new ArrayList(annotation == null ? 1 : annotation.value().length);
+            metabean.putFeature(Jsr303Features.Bean.GROUP_SEQUENCE, 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());
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("Default group sequence for bean " + beanClass.getName() + " is: " +
+                  groupSeq);
+        }
+    }
+
+    /**
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    protected boolean applyConstraint(Annotation annotation,
+                                      Class<? extends ConstraintValidator<?, ?>>[] constraintClasses,
+                                      MetaProperty prop, Class owner, AccessStrategy access,
+                                      AppendValidation appender)
+          throws IllegalAccessException, InvocationTargetException {
+
+        final ConstraintValidator validator;
+        if (constraintClasses != null) {
+            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<?, ?>>> validatorTypes =
+                  TypeUtils.getValidatorsTypes(constraintClasses);
+            final List<Type> assignableTypes = new ArrayList(constraintClasses.length);
+            fillAssignableTypes(type, validatorTypes.keySet(), assignableTypes);
+            reduceAssignableTypes(assignableTypes);
+            checkOneType(assignableTypes, type, owner, annotation, access);
+            validator = getConstraintValidatorFactory()
+                  .getInstance(validatorTypes.get(assignableTypes.get(0)));
+            validator.initialize(annotation);
+        } else {
+            validator = null;
+        }
+        final AnnotationConstraintBuilder builder = new AnnotationConstraintBuilder(
+              constraintClasses, validator, annotation, owner, access);
+        // process composed constraints:
+        // here are not other superclasses possible, because annotations do not inherit!
+        if (processAnnotations(prop, owner, annotation.annotationType(), access,
+              new AppendValidationToBuilder(builder)) || validator != null) {  // recursion!
+            appender.append(builder.getConstraintValidation());
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private 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 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 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 String stringForLocation(Class owner, AccessStrategy access) {
+        if (access != null) {
+            return access.toString();
+        } else {
+            return owner.getName();
+        }
+    }
+
+    private void fillAssignableTypes(Type type, Set<Type> validatorsTypes,
+                                     List<Type> suitableTypes) {
+        for (Type validatorType : validatorsTypes) {
+            if (TypeUtils.isAssignable(validatorType, type) &&
+                  !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 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(type, nextType)) {
+                    assignableTypes.remove(0);
+                    i--;
+                    removed = true;
+                } else if (TypeUtils.isAssignable(nextType, type)) {
+                    assignableTypes.remove(i--);
+                    removed = true;
+                }
+            }
+        } while (removed && assignableTypes.size() > 1);
+    }
+}



Mime
View raw message