Return-Path: X-Original-To: apmail-bval-commits-archive@www.apache.org Delivered-To: apmail-bval-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 462861045D for ; Mon, 26 Aug 2013 14:00:22 +0000 (UTC) Received: (qmail 56730 invoked by uid 500); 26 Aug 2013 14:00:20 -0000 Delivered-To: apmail-bval-commits-archive@bval.apache.org Received: (qmail 56656 invoked by uid 500); 26 Aug 2013 14:00:20 -0000 Mailing-List: contact commits-help@bval.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@bval.apache.org Delivered-To: mailing list commits@bval.apache.org Received: (qmail 56636 invoked by uid 99); 26 Aug 2013 14:00:19 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 26 Aug 2013 14:00:19 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 26 Aug 2013 14:00:14 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 4F2292388B56; Mon, 26 Aug 2013 13:59:31 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1517540 [7/15] - in /bval/branches/bval-11/bval-jsr: ./ src/ src/main/ src/main/appended-resources/ src/main/appended-resources/META-INF/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/bval/ src/main/j... Date: Mon, 26 Aug 2013 13:59:20 -0000 To: commits@bval.apache.org From: rmannibucau@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20130826135931.4F2292388B56@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java Mon Aug 26 13:59:15 2013 @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr; + +import org.apache.bval.cdi.BValExtension; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorFactory; +import javax.validation.ValidationException; +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; + + +/** + * Description: create constraint instances with the default / no-arg constructor
+ */ +public class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory, Closeable { + private final Collection< BValExtension.Releasable> releasables = new CopyOnWriteArrayList>(); + private Boolean useCdi = null; // store it to avoid NoClassDefFoundError when cdi is not present (it is slow) + lazily (to wait cdi is started) + + /** + * Instantiate a Constraint. + * + * @return Returns a new Constraint instance + * The ConstraintFactory is not responsible for calling Constraint#initialize + */ + public > T getInstance(final Class constraintClass) { + if (useCdi == null) { + synchronized (this) { + if (useCdi == null) { + try { + useCdi = BValExtension.getInstance() != null && BValExtension.getInstance().getBeanManager() != null; + } catch (final NoClassDefFoundError error) { + useCdi = false; + } catch (final Exception e) { + useCdi = false; + } + } + } + } + + // 2011-03-27 jw: Do not use PrivilegedAction. + // Otherwise any user code would be executed with the privileges of this class. + try { + if (useCdi) { + try { + final BValExtension.Releasable instance = BValExtension.inject(constraintClass); + releasables.add(instance); + return instance.getInstance(); + } catch (final Exception e) { + return constraintClass.newInstance(); + } catch (final NoClassDefFoundError error) { + return constraintClass.newInstance(); + } + } + return constraintClass.newInstance(); + } catch (final Exception ex) { + throw new ValidationException("Cannot instantiate : " + constraintClass, ex); + } + } + + public void releaseInstance(final ConstraintValidator instance) { + // no-op + } + + public void close() throws IOException { + for (final BValExtension.Releasable releasable : releasables) { + // ensure to call this callback + releaseInstance(ConstraintValidator.class.cast(releasable.getInstance())); + releasable.release(); + } + releasables.clear(); + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.el.MessageEvaluator; +import org.apache.bval.util.reflection.Reflection; +import org.apache.commons.lang3.ArrayUtils; + +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.logging.Level; +import java.util.logging.Logger; +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.
+ */ +public class DefaultMessageInterpolator implements MessageInterpolator { + private static final Logger log = Logger.getLogger(DefaultMessageInterpolator.class.getName()); + private static final boolean LOG_FINEST = log.isLoggable(Level.FINEST); + private static final String DEFAULT_VALIDATION_MESSAGES = "org.apache.bval.jsr.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 userBundlesMap = + new ConcurrentHashMap(); + + /** Builtin resource bundles hashed against their locale. */ + private final Map defaultBundlesMap = new ConcurrentHashMap(); + + private final MessageEvaluator evaluator; + + /** + * Create a new DefaultMessageInterpolator instance. + */ + public DefaultMessageInterpolator() { + this(null); + } + + /** + * Create a new DefaultMessageInterpolator instance. + * @param resourceBundle + */ + public DefaultMessageInterpolator(ResourceBundle resourceBundle) { + defaultLocale = Locale.getDefault(); + + // feed the cache with defaults at least + findDefaultResourceBundle(defaultLocale); + if (resourceBundle == null) { + findUserResourceBundle(defaultLocale); + } else { + userBundlesMap.put(defaultLocale, resourceBundle); + } + + MessageEvaluator ev = null; + try { + ev = MessageEvaluator.class.cast(getClass().getClassLoader().loadClass("org.apache.bval.el.ELFacade").newInstance()); + } catch (final Throwable e) { // can be exception or error + // no-op + } + evaluator = ev; + } + + /** {@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, context.getValidatedValue()); + } + + /** + * Runs the message interpolation according to algorithm specified in JSR 303. + *
+ * Note: + *
+ * 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 Locale to use for the resource bundle. + * @return the interpolated message. + */ + private String interpolateMessage(String message, + Map annotationParameters, + Locale locale, Object validatedValue) { + 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); + + // EL handling + if (evaluator != null) { + resolvedMessage = evaluator.interpolate(resolvedMessage, annotationParameters, validatedValue); + } + + // curly braces need to be scaped in the original msg, so unescape them now + resolvedMessage = resolvedMessage.replace( "\\{", "{" ).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 null if none is found. + */ + private ResourceBundle getFileBasedResourceBundle(Locale locale) { + ResourceBundle rb = null; + final ClassLoader classLoader = Reflection.INSTANCE.getClassLoader(DefaultMessageInterpolator.class); + 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 (LOG_FINEST) { + 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 (final MissingResourceException e) { + log.fine(message); + } + return rb; + } + + private String replaceVariables(String message, ResourceBundle bundle, Locale locale, + boolean recurse) { + final Matcher matcher = messageParameterPattern.matcher(message); + final StringBuffer sb = new StringBuffer(64); + String resolvedParameterValue; + while (matcher.find()) { + final 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(final String message, + final Map 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 (final 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("$", "\\$"); + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java Mon Aug 26 13:59:15 2013 @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.bval.jsr; + + +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> getValidationProviders() { + List> 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 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 + final ValidationProvider vp; + if (System.getSecurityManager() == null) { + try { + vp = (ValidationProvider) provider.newInstance(); + } catch (final Exception ex) { + throw new ValidationException("Cannot instantiate : " + provider, ex); + } + } else { + vp = AccessController.doPrivileged(new PrivilegedAction>() { + 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; + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/ElementDescriptorImpl.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/ElementDescriptorImpl.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/ElementDescriptorImpl.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/ElementDescriptorImpl.java Mon Aug 26 13:59:15 2013 @@ -0,0 +1,194 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.bval.jsr; + +import org.apache.bval.jsr.groups.Group; +import org.apache.bval.model.MetaBean; +import org.apache.bval.model.Validation; + +import javax.validation.ConstraintDeclarationException; +import javax.validation.metadata.ConstraintDescriptor; +import javax.validation.metadata.ElementDescriptor; +import javax.validation.metadata.GroupConversionDescriptor; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Description: MetaData class
+ */ +public abstract class ElementDescriptorImpl implements ElementDescriptor { + private final Set groupConversions = new CopyOnWriteArraySet(); + private boolean cascaded; + private final Collection validated = new CopyOnWriteArraySet(); + + /** + * Get a set of {@link ConstraintDescriptor}s from the specified array of + * {@link Validation}s. + * + * @param validations + * @return {@link ConstraintDescriptor} set + */ + protected static Set> getConstraintDescriptors(final Validation[] validations) { + final Set> result = new HashSet>(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> constraintDescriptors; + + private final Map groupMapping = new HashMap(); + + /** + * 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> getConstraintDescriptors() { + return constraintDescriptors.isEmpty() ? Collections.> emptySet() : Collections + .unmodifiableSet(constraintDescriptors); + } + + /** + * Get the mutable {@link ConstraintDescriptor} {@link Set}. + * + * @return Set of {@link ConstraintDescriptor} + */ + public Set> 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> constraintDescriptors) { + this.constraintDescriptors = constraintDescriptors; + } + + /** + * Get the model {@link MetaBean} used. + * + * @return MetaBean + */ + public MetaBean getMetaBean() { + return metaBean; + } + + public void addGroupMapping(final Group from, final Group to) { + groupMapping.put(from, to); + } + + public Group mapGroup(final Group current) { + final Group mapping = groupMapping.get(current); + if (mapping != null) { + return mapping; + } + return current; + } + + public Set getGroupConversions() { + return groupConversions; + } + + public void addGroupConversion(final GroupConversionDescriptor descriptor) { + groupConversions.add(descriptor); + final Group from = new Group(descriptor.getFrom()); + if (mapGroup(from) != from) { // ref == is fine + throw new ConstraintDeclarationException("You can't map twice from the same group"); + } + addGroupMapping(from, new Group(descriptor.getTo())); + } + + public boolean isCascaded() { + return cascaded; + } + + public void setCascaded(final boolean cascaded) { + this.cascaded = cascaded; + } + + public boolean isValidated(final Object object) { + return validated.contains(object); + } + + public void setValidated(final Object object) { + this.validated.add(object); + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +/** + * Class that stores the needed properties to avoid circular paths when + * validating an object graph. + *

+ * These properties are: + *

    + *
  • The ref of the bean to which the validation would be applied.
  • + *
  • The current group being validated.
  • + *
+ * + * 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; + } + + +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContext.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContext.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContext.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContext.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + + +import org.apache.bval.jsr.groups.Group; +import org.apache.bval.jsr.groups.Groups; +import org.apache.bval.jsr.util.PathImpl; +import org.apache.bval.model.MetaBean; +import org.apache.bval.model.ValidationContext; + +import javax.validation.ConstraintValidator; +import javax.validation.ElementKind; +import javax.validation.MessageInterpolator; +import javax.validation.ParameterNameProvider; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * Description: JSR-303 {@link ValidationContext} extension.
+ */ +public interface GroupValidationContext + extends ValidationContext> { + + /** + * Get the groups of this {@link GroupValidationContext}. + * @return the groups in their sequence for validation + */ + Groups getGroups(); + + void setCurrentGroups(Groups groups); + + /** + * 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); + + void setKind(ElementKind type); + + ElementKind getElementKind(); + + Object getReturnValue(); + + Object[] getParameters(); + + void setParameters(Object[] parameters); + + void setReturnValue(Object returnValue); + + ParameterNameProvider getParameterNameProvider(); + + void setMethod(Method method); + + Method getMethod(); + + void setConstructor(Constructor method); + + Constructor getConstructor(); + + void moveDown(Path.Node node); +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContextImpl.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContextImpl.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContextImpl.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/GroupValidationContextImpl.java Mon Aug 26 13:59:15 2013 @@ -0,0 +1,369 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.bval.jsr; + +import org.apache.bval.BeanValidationContext; +import org.apache.bval.jsr.groups.Group; +import org.apache.bval.jsr.groups.Groups; +import org.apache.bval.jsr.resolver.CachingTraversableResolver; +import org.apache.bval.jsr.util.NodeImpl; +import org.apache.bval.jsr.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.ElementKind; +import javax.validation.MessageInterpolator; +import javax.validation.ParameterNameProvider; +import javax.validation.Path; +import javax.validation.TraversableResolver; +import javax.validation.ValidationException; +import javax.validation.metadata.ConstraintDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +/** + * Description: instance per validation process, not thread-safe
+ */ +final class GroupValidationContextImpl extends BeanValidationContext> implements + GroupValidationContext, MessageInterpolator.Context { + + private final MessageInterpolator messageResolver; + private final PathImpl path; + private final MetaBean rootMetaBean; + private final ParameterNameProvider parameterNameProvider; + + /** + * 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 validatedConstraints = new HashSet(); + + private ConstraintValidation constraintValidation; + private final TraversableResolver traversableResolver; + + private Object[] parameters; + private Object returnValue; + private Method method; + private Constructor constructor; + + /** + * Create a new GroupValidationContextImpl instance. + * + * @param listener + * @param aMessageResolver + * @param traversableResolver + * @param parameterNameProvider + * @param rootMetaBean + */ + public GroupValidationContextImpl(ConstraintValidationListener listener, MessageInterpolator aMessageResolver, + TraversableResolver traversableResolver, ParameterNameProvider parameterNameProvider, MetaBean rootMetaBean) { + // inherited variable 'validatedObjects' is of type: + // HashMap> in this class + super(listener, new HashMap>()); + this.messageResolver = aMessageResolver; + this.traversableResolver = CachingTraversableResolver.cacheFor(traversableResolver); + this.parameterNameProvider = parameterNameProvider; + this.rootMetaBean = rootMetaBean; + this.path = PathImpl.create(); + } + + /** + * {@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)); + } + } + + public void setKind(final ElementKind type) { + path.getLeafNode().setKind(type); + } + + /** + * {@inheritDoc} + */ + @Override + public void moveDown(MetaProperty prop, AccessStrategy access) { + moveDown(prop.getName()); + super.moveDown(prop, access); + } + + @Override + public void moveDown(final String prop) { + path.addProperty(prop); + } + + public void moveDown(final Path.Node node) { + path.addNode(node); + } + + /** + * {@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 validatedPathsForGBI = (Set) validatedObjects.get(gbi); + if (validatedPathsForGBI == null) { + validatedPathsForGBI = new HashSet(); + 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; + } + + public void setCurrentGroups(final Groups g) { + groups = g; + } + + /** + * {@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(); + } + } + + public T unwrap(Class type) { + if (type.isInstance(this)) { + return type.cast(this); + } + throw new ValidationException("Type " + type + " not supported"); + } + + /** + * {@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; + } + + public ElementKind getElementKind() { + return path.getLeafNode().getKind(); + } + + public Object getReturnValue() { + return returnValue; + } + + public Object[] getParameters() { + return parameters; + } + + public void setParameters(final Object[] parameters) { + this.parameters = parameters; + } + + public void setReturnValue(final Object returnValue) { + this.returnValue = returnValue; + } + + public ParameterNameProvider getParameterNameProvider() { + return parameterNameProvider; + } + + public void setMethod(final Method method) { + this.method = method; + } + + public Method getMethod() { + return method; + } + + public Constructor getConstructor() { + return constructor; + } + + public void setConstructor(final Constructor constructor) { + this.constructor = constructor; + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/IncompatiblePropertyValueException.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/IncompatiblePropertyValueException.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/IncompatiblePropertyValueException.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/IncompatiblePropertyValueException.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +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); + } + +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/InvocableElementDescriptor.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/InvocableElementDescriptor.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/InvocableElementDescriptor.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/InvocableElementDescriptor.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.model.MetaBean; +import org.apache.bval.model.Validation; + +import javax.validation.metadata.ConstraintDescriptor; +import javax.validation.metadata.CrossParameterDescriptor; +import javax.validation.metadata.ElementDescriptor; +import javax.validation.metadata.ParameterDescriptor; +import javax.validation.metadata.ReturnValueDescriptor; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +public class InvocableElementDescriptor extends ElementDescriptorImpl implements ProcedureDescriptor { + private static final CopyOnWriteArraySet> NO_CONSTRAINTS = new CopyOnWriteArraySet>(); + + private ReturnValueDescriptor returnValueDescriptor; + private CrossParameterDescriptor crossParameterDescriptor; + private final List parameterDescriptors = new ArrayList(); + + protected InvocableElementDescriptor(final MetaBean metaBean, final Class elementClass, final Validation[] validations) { + super(metaBean, elementClass, validations); + } + + protected InvocableElementDescriptor(final Class elementClass, final Validation[] validations) { + super(elementClass, validations); + } + + /** + * {@inheritDoc} + */ + public List getParameterDescriptors() { + // index aligned + return parameterDescriptors; + } + + public void setReturnValueDescriptor(final ReturnValueDescriptor returnValueDescriptor) { + this.returnValueDescriptor = returnValueDescriptor; + } + + public CrossParameterDescriptor getCrossParameterDescriptor() { + return crossParameterDescriptor; + } + + public void setCrossParameterDescriptor(final CrossParameterDescriptor crossParameterDescriptor) { + this.crossParameterDescriptor = crossParameterDescriptor; + } + + /** + * Add the specified validations to this {@link org.apache.bval.jsr.MethodDescriptorImpl}. + * @param validations + */ + void addValidations(Collection> validations) { + getMutableConstraintDescriptors().addAll(validations); + } + + protected boolean hasConstrainedParameters() { + for (final ParameterDescriptor pd : getParameterDescriptors()) { + if (pd.isCascaded() || !pd.getConstraintDescriptors().isEmpty()) { + return true; + } + } + return getCrossParameterDescriptor().hasConstraints(); + } + + public ReturnValueDescriptor getReturnValueDescriptor() { + return returnValueDescriptor; + } + + protected boolean hasConstrainedReturnValue() { + return getReturnValueDescriptor().isCascaded() || !getReturnValueDescriptor().getConstraintDescriptors().isEmpty(); + } + + @Override + public ElementDescriptor.ConstraintFinder findConstraints() { + return new ConstraintFinderImpl(metaBean, NO_CONSTRAINTS); + } + + @Override + public Set> getConstraintDescriptors() { + return Set.class.cast(NO_CONSTRAINTS); + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrFeatures.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrFeatures.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrFeatures.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrFeatures.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.model.Features; + +/** + * Description: Contains MetaBean feature keys of additional features used in the implementation + * of JSR303
+ * + * @see org.apache.bval.model.FeaturesCapable + * @see org.apache.bval.model.Features + */ +public interface JsrFeatures { + interface Method extends Features.Property { + String MethodDescriptor = "MethodDescriptor"; + } + + interface Constructor extends Features.Property { + String ConstructorDescriptor = "ConstructorDescriptor"; + } + + /** + * 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"; + + // Collection + String ANNOTATIONS_TO_PROCESS = "annotationToProcess"; + } + + /** + * 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"; + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrMetaBeanFactory.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrMetaBeanFactory.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrMetaBeanFactory.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/JsrMetaBeanFactory.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.MetaBeanFactory; +import org.apache.bval.jsr.groups.Group; +import org.apache.bval.jsr.util.ClassHelper; +import org.apache.bval.jsr.xml.MetaConstraint; +import org.apache.bval.model.Meta; +import org.apache.bval.model.MetaBean; +import org.apache.bval.model.MetaConstructor; +import org.apache.bval.model.MetaMethod; +import org.apache.bval.model.MetaParameter; +import org.apache.bval.model.MetaProperty; +import org.apache.bval.util.AccessStrategy; +import org.apache.bval.util.FieldAccess; +import org.apache.bval.util.MethodAccess; +import org.apache.bval.util.reflection.Reflection; + +import javax.validation.ConstraintDeclarationException; +import javax.validation.GroupDefinitionException; +import javax.validation.GroupSequence; +import javax.validation.groups.ConvertGroup; +import javax.validation.groups.Default; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Description: process the class annotations for JSR303 constraint validations to build the MetaBean with information + * from annotations and JSR303 constraint mappings (defined in xml)
+ */ +public class JsrMetaBeanFactory implements MetaBeanFactory { + /** Shared log instance */ + // of dubious utility as it's static :/ + protected static final Logger log = Logger.getLogger(JsrMetaBeanFactory.class.getName()); + + /** {@link ApacheFactoryContext} used */ + protected final ApacheFactoryContext factoryContext; + + /** + * {@link AnnotationProcessor} used. + */ + protected AnnotationProcessor annotationProcessor; + + /** + * Create a new Jsr303MetaBeanFactory instance. + * + * @param factoryContext + */ + public JsrMetaBeanFactory(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> classSequence = new ArrayList>(); + 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); + if (eachClass == Serializable.class || eachClass == Cloneable.class) { + continue; + } + + 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 Collection missingValid = new ArrayList(); + + final Field[] fields = Reflection.INSTANCE.getDeclaredFields(beanClass); + for (final 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); + } + + if (field.getAnnotation(ConvertGroup.class) != null) { + missingValid.add(field.getName()); + } + } + } + final Method[] methods = Reflection.INSTANCE.getDeclaredMethods(beanClass); + for (final 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); + } + } + } + } + + addXmlConstraints(beanClass, metabean); + + for (final String name : missingValid) { + final MetaProperty metaProperty = metabean.getProperty(name); + if (metaProperty != null && metaProperty.getFeature(JsrFeatures.Property.REF_CASCADE) == null) { + throw new ConstraintDeclarationException("@ConvertGroup needs @Valid"); + } + } + missingValid.clear(); + } + + /** + * 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 (final MetaConstraint metaConstraint : factoryContext.getFactory().getMetaConstraints(beanClass)) { + Meta meta; + AccessStrategy access = metaConstraint.getAccessStrategy(); + boolean create = false; + if (access == null) { // class level + meta = null; + } else if (access.getElementType() == ElementType.METHOD && !metaConstraint.getMember().getName().startsWith("get")) { // TODO: better getter test + final Method method = Method.class.cast(metaConstraint.getMember()); + meta = metabean.getMethod(method); + final MetaMethod metaMethod; + if (meta == null) { + meta = new MetaMethod(metabean, method); + metaMethod = MetaMethod.class.cast(meta); + metabean.addMethod(method, metaMethod); + } else { + metaMethod = MetaMethod.class.cast(meta); + } + final Integer index = metaConstraint.getIndex(); + if (index != null && index >= 0) { + MetaParameter param = metaMethod.getParameter(index); + if (param == null) { + param = new MetaParameter(metaMethod, index); + metaMethod.addParameter(index, param); + } + param.addAnnotation(metaConstraint.getAnnotation()); + } else { + metaMethod.addAnnotation(metaConstraint.getAnnotation()); + } + continue; + } else if (access.getElementType() == ElementType.CONSTRUCTOR){ + final Constructor constructor = Constructor.class.cast(metaConstraint.getMember()); + meta = metabean.getConstructor(constructor); + final MetaConstructor metaConstructor; + if (meta == null) { + meta = new MetaConstructor(metabean, constructor); + metaConstructor = MetaConstructor.class.cast(meta); + metabean.addConstructor(constructor, metaConstructor); + } else { + metaConstructor = MetaConstructor.class.cast(meta); + } + final Integer index = metaConstraint.getIndex(); + if (index != null && index >= 0) { + MetaParameter param = metaConstructor.getParameter(index); + if (param == null) { + param = new MetaParameter(metaConstructor, index); + metaConstructor.addParameter(index, param); + } + param.addAnnotation(metaConstraint.getAnnotation()); + } else { + metaConstructor.addAnnotation(metaConstraint.getAnnotation()); + } + continue; + } else { // property level + meta = metabean.getProperty(access.getPropertyName()); + create = meta == null; + if (create) { + meta = addMetaProperty(metabean, access); + } + } + if (!annotationProcessor.processAnnotation(metaConstraint.getAnnotation(), meta, beanClass, + metaConstraint.getAccessStrategy(), new AppendValidationToMeta(meta == null ? metabean : meta), false) + && create) { + metabean.putProperty(access.getPropertyName(), null); + } + } + for (final AccessStrategy access : factoryContext.getFactory().getValidAccesses(beanClass)) { + if (access.getElementType() == ElementType.PARAMETER) { + continue; + } + + 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, JsrFeatures.Bean.GROUP_SEQUENCE); + } + + private void processGroupSequence(Class beanClass, MetaBean metabean, String key) { + GroupSequence annotation = beanClass.getAnnotation(GroupSequence.class); + List groupSeq = metabean.getFeature(key); + if (groupSeq == null) { + groupSeq = metabean.initFeature(key, new ArrayList(annotation == null ? 1 : annotation.value().length)); + } + 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 (final 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; + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptor.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptor.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptor.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptor.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import javax.validation.metadata.ElementDescriptor; +import javax.validation.metadata.ParameterDescriptor; +import java.util.List; + +/** + * Description: This class will disappear when such + * functionality is part of the JSR303 specification.
+ */ +public interface MethodDescriptor extends ElementDescriptor { + /** + * Get the {@link javax.validation.metadata.ParameterDescriptor}s for this {@link org.apache.bval.jsr.MethodDescriptor}. + * @return {@link java.util.List} of {@link javax.validation.metadata.ParameterDescriptor} + */ + List getParameterDescriptors(); //index aligned + + /** + * Learn whether the referenced method should be validated. + * @return boolean + */ + boolean isCascaded(); + +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptorImpl.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptorImpl.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptorImpl.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/MethodDescriptorImpl.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.model.MetaBean; +import org.apache.bval.model.MetaMethod; +import org.apache.bval.model.Validation; + +import java.lang.reflect.Method; + +/** + * Description: {@link MethodDescriptor} implementation.
+ */ +public class MethodDescriptorImpl extends InvocableElementDescriptor implements javax.validation.metadata.MethodDescriptor, ProcedureDescriptor { + private static final Validation[] EMPTY_VALIDATION = new Validation[0]; + + private final String name; + + protected MethodDescriptorImpl(final MetaBean metaBean, final Validation[] validations, final Method method) { + super(metaBean, method.getReturnType(), validations); + name = method.getName(); + } + + public MethodDescriptorImpl(final MetaBean bean, final MetaMethod metaMethod) { + super(bean, metaMethod.getMethod().getReturnType(), EMPTY_VALIDATION); + setCascaded(false); + this.name = metaMethod.getMethod().getName(); + } + + public String getName() { + return name; + } + + public boolean hasConstrainedParameters() { + return super.hasConstrainedParameters(); + } + + public boolean hasConstrainedReturnValue() { + return super.hasConstrainedReturnValue(); + } + + @Override + public boolean hasConstraints() { + return false; + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.jsr.util.LeafNodeBuilderCustomizableContextImpl; +import org.apache.bval.jsr.util.NodeImpl; +import org.apache.bval.jsr.util.PathImpl; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.ElementKind; + +public class NodeBuilderCustomizableContextImpl implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext { + private final PathImpl path; + private final ConstraintValidatorContextImpl context; + private final String template; + + public NodeBuilderCustomizableContextImpl(final ConstraintValidatorContextImpl parent, final String messageTemplate, final PathImpl propertyPath) { + context = parent; + template = messageTemplate; + path = propertyPath; + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() { + path.getLeafNode().setInIterable(true); + return new NodeContextBuilderImpl(context, template, path); + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name) { + path.addNode(new NodeImpl(name)); + return this; + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(String name) { + final NodeImpl node = new NodeImpl.PropertyNodeImpl(name); + node.setKind(ElementKind.PROPERTY); + path.addNode(node); + return this; + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() { + final NodeImpl node = new NodeImpl.BeanNodeImpl(); + node.setKind(ElementKind.BEAN); + path.addNode(node); + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } +} Added: bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java URL: http://svn.apache.org/viewvc/bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java?rev=1517540&view=auto ============================================================================== --- bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java (added) +++ bval/branches/bval-11/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java Mon Aug 26 13:59:15 2013 @@ -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.jsr; + +import org.apache.bval.jsr.util.LeafNodeBuilderCustomizableContextImpl; +import org.apache.bval.jsr.util.NodeBuilderDefinedContextImpl; +import org.apache.bval.jsr.util.NodeImpl; +import org.apache.bval.jsr.util.PathImpl; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.ElementKind; + +public class NodeContextBuilderImpl implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder { + private final PathImpl path; + private final String template; + private final ConstraintValidatorContextImpl context; + + public NodeContextBuilderImpl(final ConstraintValidatorContextImpl context, final String template, final PathImpl path) { + this.context = context; + this.template = template; + this.path = path; + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(Object key) { + path.getLeafNode().setKey(key); + return new NodeBuilderDefinedContextImpl(context, template, path); + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(Integer index) { + path.getLeafNode().setIndex(index); + return new NodeBuilderDefinedContextImpl(context, template, path); + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name) { + return new NodeBuilderCustomizableContextImpl(context, template, path).addNode(name); + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode(String name) { + return new NodeBuilderCustomizableContextImpl(context, template, path).addPropertyNode(name); + } + + public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() { + final NodeImpl node = new NodeImpl.BeanNodeImpl(); + node.setKind(ElementKind.BEAN); + path.addNode(node); + return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } +}