http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java index c367b8e..91ae20d 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ConstraintViolationImpl.java @@ -20,15 +20,20 @@ import javax.validation.ConstraintViolation; import javax.validation.Path; import javax.validation.ValidationException; import javax.validation.metadata.ConstraintDescriptor; + +import org.apache.bval.util.Exceptions; + import java.io.Serializable; import java.lang.annotation.ElementType; import java.util.Arrays; +import java.util.Objects; /** * Description: Describe a constraint validation defect.<br/> - * From rootBean and propertyPath, it is possible to rebuild the context of the failure + * From rootBean and propertyPath, it is possible to rebuild the context of the + * failure */ -class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable { +public class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable { /** Serialization version */ private static final long serialVersionUID = 1L; @@ -49,8 +54,11 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable /** * Create a new ConstraintViolationImpl instance. - * @param messageTemplate - message reason (raw message) - * @param message - interpolated message (locale specific) + * + * @param messageTemplate + * - message reason (raw message) + * @param message + * - interpolated message (locale specific) * @param rootBean * @param leafBean * @param propertyPath @@ -79,8 +87,8 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable } /** - * {@inheritDoc} - * former name getInterpolatedMessage() + * {@inheritDoc} former name getInterpolatedMessage() + * * @return The interpolated error message for this constraint violation. */ @Override @@ -98,6 +106,7 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable /** * {@inheritDoc} + * * @return Root bean being validated */ @Override @@ -133,6 +142,7 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable /** * {@inheritDoc} + * * @return The value failing to pass the constraint */ @Override @@ -142,8 +152,9 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable /** * {@inheritDoc} - * @return the property path to the value from <code>rootBean</code> - * Null if the value is the rootBean itself + * + * @return the property path to the value from <code>rootBean</code> Null if + * the value is the rootBean itself */ @Override public Path getPropertyPath() { @@ -160,10 +171,8 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable @Override public <U> U unwrap(Class<U> type) { - if (type.isInstance(this)) { - return type.cast(this); - } - throw new ValidationException("Type " + type + " is not supported"); + Exceptions.raiseUnless(type.isInstance(this), ValidationException::new, "Type %s is not supported", type); + return type.cast(this); } /** @@ -171,45 +180,28 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable */ @Override public String toString() { - return "ConstraintViolationImpl{" + "rootBean=" + rootBean + ", propertyPath='" + propertyPath + '\'' - + ", message='" + message + '\'' + ", leafBean=" + leafBean + ", value=" + value + '}'; + return String.format("%s{rootBean=%s, propertyPath='%s', message='%s', leafBean=%s, value=%s}", + ConstraintViolationImpl.class.getSimpleName(), rootBean, propertyPath, message, leafBean, value); } @Override public boolean equals(Object o) { - if (this == o) + if (this == o) { return true; - if (o == null || getClass() != o.getClass()) + } + if (o == null || !getClass().equals(o.getClass())) { return false; + } - ConstraintViolationImpl that = (ConstraintViolationImpl) o; - - if (constraintDescriptor != null ? !constraintDescriptor.equals(that.constraintDescriptor) - : that.constraintDescriptor != null) - return false; - if (elementType != that.elementType) - return false; - if (leafBean != null ? !leafBean.equals(that.leafBean) : that.leafBean != null) - return false; - if (message != null ? !message.equals(that.message) : that.message != null) - return false; - if (messageTemplate != null ? !messageTemplate.equals(that.messageTemplate) : that.messageTemplate != null) - return false; - // Probably incorrect - comparing Object[] arrays with Arrays.equals - if (!Arrays.equals(parameters, that.parameters)) - return false; - if (propertyPath != null ? !propertyPath.equals(that.propertyPath) : that.propertyPath != null) - return false; - if (returnValue != null ? !returnValue.equals(that.returnValue) : that.returnValue != null) - return false; - if (rootBean != null ? !rootBean.equals(that.rootBean) : that.rootBean != null) - return false; - if (rootBeanClass != null ? !rootBeanClass.equals(that.rootBeanClass) : that.rootBeanClass != null) - return false; - if (value != null ? !value.equals(that.value) : that.value != null) - return false; + @SuppressWarnings("rawtypes") + final ConstraintViolationImpl that = (ConstraintViolationImpl) o; - return true; + return Objects.equals(constraintDescriptor, that.constraintDescriptor) && elementType == that.elementType + && Objects.equals(leafBean, that.leafBean) && Objects.equals(message, that.message) + && Objects.equals(messageTemplate, that.messageTemplate) && Arrays.equals(parameters, that.parameters) + && Objects.equals(propertyPath, that.propertyPath) && Objects.equals(returnValue, that.returnValue) + && Objects.equals(rootBean, that.rootBean) && Objects.equals(rootBeanClass, that.rootBeanClass) + && Objects.equals(value, that.value); } @Override @@ -217,18 +209,10 @@ class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable return hashCode; } - public int computeHashCode() { - int result = messageTemplate != null ? messageTemplate.hashCode() : 0; - result = 31 * result + (message != null ? message.hashCode() : 0); - result = 31 * result + (rootBean != null ? rootBean.hashCode() : 0); - result = 31 * result + (rootBeanClass != null ? rootBeanClass.hashCode() : 0); - result = 31 * result + (leafBean != null ? leafBean.hashCode() : 0); - result = 31 * result + (value != null ? value.hashCode() : 0); - result = 31 * result + (propertyPath != null ? propertyPath.hashCode() : 0); - result = 31 * result + (elementType != null ? elementType.hashCode() : 0); - result = 31 * result + (constraintDescriptor != null ? constraintDescriptor.hashCode() : 0); - result = 31 * result + (returnValue != null ? returnValue.hashCode() : 0); - result = 31 * result + (parameters != null ? Arrays.hashCode(parameters) : 0); + private int computeHashCode() { + int result = Objects.hash(messageTemplate, message, rootBean, rootBeanClass, leafBean, value, propertyPath, + elementType, constraintDescriptor, returnValue); + result = 31 * result + (parameters == null ? 0 : Arrays.hashCode(parameters)); return result; } }
http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java index 4aca48a..9474705 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultConstraintValidatorFactory.java @@ -32,8 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * Description: create constraint instances with the default / no-arg constructor <br/> */ public class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory, Closeable { - private final Collection<BValExtension.Releasable<?>> releasables = - new CopyOnWriteArrayList<BValExtension.Releasable<?>>(); + private final Collection<BValExtension.Releasable<?>> releasables = new CopyOnWriteArrayList<>(); private volatile Boolean useCdi = null; // store it to avoid NoClassDefFoundError when cdi is not present (it is slow) + lazily (to wait cdi is started) /** @@ -49,9 +48,7 @@ public class DefaultConstraintValidatorFactory implements ConstraintValidatorFac if (useCdi == null) { try { useCdi = BValExtension.getBeanManager() != null; - } catch (final NoClassDefFoundError error) { - useCdi = Boolean.FALSE; - } catch (final Exception e) { + } catch (NoClassDefFoundError | Exception error) { useCdi = Boolean.FALSE; } } @@ -69,10 +66,7 @@ public class DefaultConstraintValidatorFactory implements ConstraintValidatorFac return instance.getInstance(); } throw new IllegalStateException("Can't create " + constraintClass.getName()); - } catch (final Exception e) { - return constraintClass.newInstance(); - } catch (final NoClassDefFoundError error) { - return constraintClass.newInstance(); + } catch (Exception | NoClassDefFoundError e) { } } return constraintClass.newInstance(); http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java index 8c77162..6a85a2a 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultMessageInterpolator.java @@ -54,10 +54,10 @@ public class DefaultMessageInterpolator implements MessageInterpolator { private Locale defaultLocale; /** User specified resource bundles hashed against their locale. */ - private final Map<Locale, ResourceBundle> userBundlesMap = new ConcurrentHashMap<Locale, ResourceBundle>(); + private final Map<Locale, ResourceBundle> userBundlesMap = new ConcurrentHashMap<>(); /** Builtin resource bundles hashed against their locale. */ - private final Map<Locale, ResourceBundle> defaultBundlesMap = new ConcurrentHashMap<Locale, ResourceBundle>(); + private final Map<Locale, ResourceBundle> defaultBundlesMap = new ConcurrentHashMap<>(); private final MessageEvaluator evaluator; @@ -83,12 +83,12 @@ public class DefaultMessageInterpolator implements MessageInterpolator { userBundlesMap.put(defaultLocale, resourceBundle); } - MessageEvaluator ev = null; + MessageEvaluator ev; 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 + ev = null; } evaluator = ev; } @@ -170,47 +170,42 @@ public class DefaultMessageInterpolator implements MessageInterpolator { * @return the resource bundle or <code>null</code> if none is found. */ private ResourceBundle getFileBasedResourceBundle(Locale locale) { - ResourceBundle rb = null; + ResourceBundle rb; final ClassLoader classLoader = Reflection.getClassLoader(DefaultMessageInterpolator.class); if (classLoader != null) { rb = loadBundle(classLoader, locale, USER_VALIDATION_MESSAGES + " not found by thread local classloader"); - } - + } else { // 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 { + if (rb == null) { log.log(Level.FINEST, String.format("%s not found. Delegating to %s", USER_VALIDATION_MESSAGES, DEFAULT_VALIDATION_MESSAGES)); + } else { + log.log(Level.FINEST, String.format("%s found", USER_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); + return ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, classLoader); } catch (final MissingResourceException e) { log.fine(message); } - return rb; + return null; } 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); - + String resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse); matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue)); } matcher.appendTail(sb); @@ -242,13 +237,13 @@ public class DefaultMessageInterpolator implements MessageInterpolator { private String resolveParameter(String parameterName, ResourceBundle bundle, Locale locale, boolean recurse) { String parameterValue; try { - if (bundle != null) { + if (bundle == null) { + parameterValue = parameterName; + } else { parameterValue = bundle.getString(removeCurlyBrace(parameterName)); if (recurse) { parameterValue = replaceVariables(parameterValue, bundle, locale, recurse); } - } else { - parameterValue = parameterName; } } catch (final MissingResourceException e) { // return parameter itself http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java index 671b0d9..e63bdf4 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/DefaultValidationProviderResolver.java @@ -38,6 +38,11 @@ public class DefaultValidationProviderResolver implements ValidationProviderReso //TODO - Spec recommends caching per classloader private static final String SPI_CFG = "META-INF/services/javax.validation.spi.ValidationProvider"; + private static ClassLoader getCurrentClassLoader() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + return cl == null ? DefaultValidationProviderResolver.class.getClassLoader() : cl; + } + /** * {@inheritDoc} */ @@ -46,43 +51,28 @@ public class DefaultValidationProviderResolver implements ValidationProviderReso List<ValidationProvider<?>> providers = new ArrayList<ValidationProvider<?>>(); try { // get our classloader - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if (cl == null) - cl = DefaultValidationProviderResolver.class.getClassLoader(); + ClassLoader cl = getCurrentClassLoader(); // find all service provider cfgs Enumeration<URL> cfgs = cl.getResources(SPI_CFG); while (cfgs.hasMoreElements()) { final 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 - @SuppressWarnings("rawtypes") - final Class<? extends ValidationProvider> providerType = - cl.loadClass(line).asSubclass(ValidationProvider.class); - // create an instance to return - providers - .add(Reflection.newInstance(providerType.asSubclass(ValidationProvider.class))); - - } catch (ClassNotFoundException e) { - throw new ValidationException( - "Failed to load provider " + line + " configured in file " + url, e); - } + try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()), 256)) { + br.lines().filter(s -> s.charAt(0) != '#').map(String::trim).forEach(line -> { + // cfgs may contain multiple providers and/or comments + try { + // try loading the specified class + @SuppressWarnings("rawtypes") + final Class<? extends ValidationProvider> providerType = + cl.loadClass(line).asSubclass(ValidationProvider.class); + // create an instance to return + providers.add(Reflection.newInstance(providerType)); + } catch (ClassNotFoundException e) { + throw new ValidationException( + "Failed to load provider " + line + " configured in file " + url, e); } - line = br.readLine(); - } + }); } catch (IOException e) { throw new ValidationException("Error trying to read " + url, e); - } finally { - if (br != null) { - br.close(); - } } } } catch (IOException e) { http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java index 3ec666e..26391e6 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphBeanIdentity.java @@ -18,6 +18,8 @@ */ package org.apache.bval.jsr; +import java.util.Objects; + /** * Class that stores the needed properties to avoid circular paths when * validating an object graph. @@ -80,32 +82,16 @@ public class GraphBeanIdentity { */ @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; - } - - return true; + // Bean ref must be the same; Group ref must be the same + return bean == other.bean && group == other.group; } /** @@ -113,11 +99,7 @@ public class GraphBeanIdentity { */ @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()); - return result; + return Objects.hash(bean, group); } } http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java new file mode 100644 index 0000000..26350d6 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/GraphContext.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr; + +import javax.validation.Path; + +import org.apache.bval.jsr.util.NodeImpl; +import org.apache.bval.jsr.util.PathImpl; +import org.apache.bval.util.Validate; + +public class GraphContext { + + private final ApacheFactoryContext validatorContext; + private final PathImpl path; + private final Object value; + private final GraphContext parent; + + public GraphContext(ApacheFactoryContext validatorContext, PathImpl path, Object value) { + this(validatorContext, path, value, null); + } + + private GraphContext(ApacheFactoryContext validatorContext, PathImpl path, Object value, GraphContext parent) { + super(); + this.validatorContext = Validate.notNull(validatorContext, "validatorContext"); + this.path = Validate.notNull(path, "path"); + this.value = value; + this.parent = parent; + } + + public ApacheFactoryContext getValidatorContext() { + return validatorContext; + } + + public PathImpl getPath() { + return PathImpl.copy(path); + } + + public Object getValue() { + return value; + } + + public GraphContext child(NodeImpl node, Object value) { + Validate.notNull(node, "node"); + final PathImpl p = PathImpl.copy(path); + p.addNode(node); + return new GraphContext(validatorContext, p, value, this); + } + + public GraphContext child(Path p, Object value) { + Validate.notNull(p, "Path"); + final PathImpl impl = PathImpl.copy(p); + Validate.isTrue(impl.isSubPathOf(path), "%s is not a subpath of %s", p, path); + return new GraphContext(validatorContext, impl, value, this); + } + + public boolean isRoot() { + return parent == null; + } + + public boolean isRecursive() { + GraphContext c = parent; + while (c != null) { + if (c.value == value) { + return true; + } + c = c.parent; + } + return false; + } + + public GraphContext getParent() { + return parent; + } + + @Override + public String toString() { + return String.format("%s: %s at '%s'", getClass().getSimpleName(), value, path); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java new file mode 100644 index 0000000..8200bfe --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeBuilderCustomizableContextImpl.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.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.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; +import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext; +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; + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() { + path.getLeafNode().setInIterable(true); + return new NodeContextBuilderImpl(context, template, path); + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name) { + path.addNode(new NodeImpl(name)); + return this; + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode( + String name) { + final NodeImpl node = new NodeImpl.PropertyNodeImpl(name); + path.addNode(node); + return this; + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() { + final NodeImpl node = new NodeImpl.BeanNodeImpl(); + path.addNode(node); + return null; +// return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + @Override + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } + + @Override + public NodeBuilderCustomizableContext inContainer(Class<?> containerClass, Integer typeArgumentIndex) { + // TODO Auto-generated method stub + return null; + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + // TODO Auto-generated method stub + return null; + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java new file mode 100644 index 0000000..d1f191e --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/NodeContextBuilderImpl.java @@ -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 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.ConstraintValidatorContext.ConstraintViolationBuilder.ContainerElementNodeBuilderCustomizableContext; +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; + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(Object key) { + path.getLeafNode().setKey(key); + return null; +// return new NodeBuilderDefinedContextImpl(context, template, path); + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(Integer index) { + path.getLeafNode().setIndex(index); + return null; +// return new NodeBuilderDefinedContextImpl(context, template, path); + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name) { + return new NodeBuilderCustomizableContextImpl(context, template, path).addNode(name); + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addPropertyNode( + String name) { + return new NodeBuilderCustomizableContextImpl(context, template, path).addPropertyNode(name); + } + + @Override + public ConstraintValidatorContext.ConstraintViolationBuilder.LeafNodeBuilderCustomizableContext addBeanNode() { + final NodeImpl node = new NodeImpl.BeanNodeImpl(); + path.addNode(node); + return null; +// return new LeafNodeBuilderCustomizableContextImpl(context, template, path); + } + + @Override + public ConstraintValidatorContext addConstraintViolation() { + context.addError(template, path); + return context; + } + + @Override + public ContainerElementNodeBuilderCustomizableContext addContainerElementNode(String name, Class<?> containerType, + Integer typeArgumentIndex) { + // TODO Auto-generated method stub + return null; + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java index 187fd7e..65f3ecc 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ParameterDescriptorImpl.java @@ -21,6 +21,7 @@ import org.apache.bval.jsr.groups.GroupConversionDescriptorImpl; import org.apache.bval.model.MetaBean; import org.apache.bval.model.Validation; +import javax.validation.metadata.ContainerElementTypeDescriptor; import javax.validation.metadata.GroupConversionDescriptor; import javax.validation.metadata.ParameterDescriptor; import java.util.Set; @@ -86,4 +87,10 @@ public class ParameterDescriptorImpl extends ElementDescriptorImpl implements Pa groupConversions.add(new GroupConversionDescriptorImpl(from, to)); super.addGroupMapping(from, to); } + + @Override + public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() { + // TODO Auto-generated method stub + return null; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java index 7f7c56d..03cf5de 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/PropertyDescriptorImpl.java @@ -21,6 +21,9 @@ package org.apache.bval.jsr; import org.apache.bval.model.Features; import org.apache.bval.model.MetaProperty; +import java.util.Set; + +import javax.validation.metadata.ContainerElementTypeDescriptor; import javax.validation.metadata.PropertyDescriptor; /** @@ -67,4 +70,10 @@ class PropertyDescriptorImpl extends ElementDescriptorImpl implements PropertyDe return "PropertyDescriptorImpl{" + "returnType=" + elementClass + ", propertyPath='" + propertyPath + '\'' + '}'; } + + @Override + public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() { + // TODO Auto-generated method stub + return null; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java index b1fc72d..a6faa9b 100644 --- a/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ReturnValueDescriptorImpl.java @@ -18,8 +18,10 @@ package org.apache.bval.jsr; import org.apache.bval.model.MetaBean; +import javax.validation.metadata.ContainerElementTypeDescriptor; import javax.validation.metadata.ReturnValueDescriptor; import java.util.Collection; +import java.util.Set; public class ReturnValueDescriptorImpl extends ElementDescriptorImpl implements ReturnValueDescriptor { public ReturnValueDescriptorImpl(final MetaBean metaBean, Class<?> returnType, @@ -32,4 +34,10 @@ public class ReturnValueDescriptorImpl extends ElementDescriptorImpl implements public boolean hasConstraints() { return false; } + + @Override + public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() { + // TODO Auto-generated method stub + return null; + } } http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java b/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java new file mode 100644 index 0000000..606e191 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/ValidatorImpl.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.ValidationException; +import javax.validation.executable.ExecutableValidator; +import javax.validation.metadata.BeanDescriptor; + +import org.apache.bval.jsr.job.ValidationJobFactory; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.Reflection; + +public class ValidatorImpl implements CascadingPropertyValidator, ExecutableValidator { + + private final ApacheFactoryContext validatorContext; + private final ValidationJobFactory validationJobFactory; + + ValidatorImpl(ApacheFactoryContext validatorContext) { + super(); + this.validatorContext = Validate.notNull(validatorContext, "validatorContext"); + this.validationJobFactory = new ValidationJobFactory(validatorContext); + } + + @Override + public BeanDescriptor getConstraintsForClass(Class<?> clazz) { + return validatorContext.getDescriptorManager().getBeanDescriptor(clazz); + } + + @Override + public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) { + return validationJobFactory.validateBean(object, groups).getResults(); + } + + @Override + public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade, + Class<?>... groups) { + return validationJobFactory.validateProperty(object, propertyName, groups).cascade(cascade).getResults(); + } + + @Override + public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value, + boolean cascade, Class<?>... groups) { + return validationJobFactory.validateValue(beanType, propertyName, value, groups).cascade(cascade).getResults(); + } + + @Override + public ExecutableValidator forExecutables() { + return this; + } + + @Override + public <T> Set<ConstraintViolation<T>> validateParameters(T object, Method method, Object[] parameterValues, + Class<?>... groups) { + return validationJobFactory.validateParameters(object, method, parameterValues, groups).getResults(); + } + + @Override + public <T> Set<ConstraintViolation<T>> validateReturnValue(T object, Method method, Object returnValue, + Class<?>... groups) { + return validationJobFactory.validateReturnValue(object, method, returnValue, groups).getResults(); + } + + @Override + public <T> Set<ConstraintViolation<T>> validateConstructorParameters(Constructor<? extends T> constructor, + Object[] parameterValues, Class<?>... groups) { + return validationJobFactory.<T> validateConstructorParameters(constructor, parameterValues, groups) + .getResults(); + } + + @Override + public <T> Set<ConstraintViolation<T>> validateConstructorReturnValue(Constructor<? extends T> constructor, + T createdObject, Class<?>... groups) { + return validationJobFactory.<T> validateConstructorReturnValue(constructor, createdObject, groups).getResults(); + } + + @Override + public <T> T unwrap(Class<T> type) { + // FIXME 2011-03-27 jw: + // This code is unsecure. + // It should allow only a fixed set of classes. + // Can't fix this because don't know which classes this method should support. + + if (type.isAssignableFrom(getClass())) { + @SuppressWarnings("unchecked") + final T result = (T) this; + return result; + } + if (!(type.isInterface() || Modifier.isAbstract(type.getModifiers()))) { + return newInstance(type); + } + try { + final Class<?> cls = Reflection.toClass(type.getName() + "Impl"); + if (type.isAssignableFrom(cls)) { + @SuppressWarnings("unchecked") + final Class<? extends T> implClass = (Class<? extends T>) cls; + return newInstance(implClass); + } + } catch (ClassNotFoundException e) { + } + throw new ValidationException("Type " + type + " not supported"); + } + + private <T> T newInstance(final Class<T> cls) { + final Constructor<T> cons = Reflection.getDeclaredConstructor(cls, ApacheFactoryContext.class); + if (cons == null) { + throw new ValidationException("Cannot instantiate " + cls); + } + final boolean mustUnset = Reflection.setAccessible(cons, true); + try { + return cons.newInstance(validatorContext); + } catch (final Exception ex) { + throw new ValidationException("Cannot instantiate " + cls, ex); + } finally { + if (mustUnset) { + Reflection.setAccessible(cons, false); + } + } + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java new file mode 100644 index 0000000..7f52c6d --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/BeanD.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.descriptor; + +import java.lang.reflect.Type; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import javax.validation.metadata.BeanDescriptor; +import javax.validation.metadata.ConstructorDescriptor; +import javax.validation.metadata.MethodDescriptor; +import javax.validation.metadata.MethodType; +import javax.validation.metadata.PropertyDescriptor; + +import org.apache.bval.jsr.metadata.Signature; +import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.StringUtils; + +public class BeanD extends ElementD<Class<?>, MetadataReader.ForBean> implements BeanDescriptor { + + private static boolean constrainedProperty(PropertyDescriptor pd) { + return pd.hasConstraints() || pd.isCascaded(); + } + + private final Class<?> beanClass; + + private final Lazy<List<Class<?>>> groupSequence; + private final Lazy<Map<String, PropertyDescriptor>> propertiesMap; + private final Lazy<Set<PropertyDescriptor>> properties; + private final Lazy<Map<Signature, ConstructorD>> constructors; + private final Lazy<Map<Signature, MethodD>> methods; + + BeanD(MetadataReader.ForBean reader) { + super(reader); + this.beanClass = reader.meta.getHost(); + + groupSequence = new Lazy<>(reader::getGroupSequence); + propertiesMap = new Lazy<>(() -> reader.getProperties(this)); + properties = new Lazy<>(() -> propertiesMap.get().values().stream().filter(BeanD::constrainedProperty) + .collect(ToUnmodifiable.set())); + constructors = new Lazy<>(() -> reader.getConstructors(this)); + methods = new Lazy<>(() -> reader.getMethods(this)); + } + + @Override + public Class<?> getElementClass() { + return beanClass; + } + + @Override + public boolean isBeanConstrained() { + return hasConstraints() || properties.get().stream().anyMatch(DescriptorManager::isConstrained); + } + + @Override + public PropertyDescriptor getConstraintsForProperty(String propertyName) { + return Optional.ofNullable(getProperty(propertyName)).filter(BeanD::constrainedProperty).orElse(null); + } + + @Override + public Set<PropertyDescriptor> getConstrainedProperties() { + return properties.get(); + } + + @Override + public MethodDescriptor getConstraintsForMethod(String methodName, Class<?>... parameterTypes) { + return methods.get().get(new Signature(methodName, parameterTypes)); + } + + @SuppressWarnings("unlikely-arg-type") + @Override + public Set<MethodDescriptor> getConstrainedMethods(MethodType methodType, MethodType... methodTypes) { + return methods.get().values().stream().filter(EnumSet.of(methodType, methodTypes)::contains) + .collect(ToUnmodifiable.set()); + } + + @Override + public ConstructorDescriptor getConstraintsForConstructor(Class<?>... parameterTypes) { + return constructors.get().get(new Signature(beanClass.getName(), parameterTypes)); + } + + @Override + public Set<ConstructorDescriptor> getConstrainedConstructors() { + return constructors.get().values().stream().collect(ToUnmodifiable.set()); + } + + public PropertyDescriptor getProperty(String propertyName) { + Exceptions.raiseIf(StringUtils.isBlank(propertyName), IllegalArgumentException::new, + "propertyName was null/empty/blank"); + + return propertiesMap.get().get(propertyName); + } + + @Override + protected BeanD getBean() { + return this; + } + + @Override + public List<Class<?>> getGroupSequence() { + return groupSequence.get(); + } + + public final Type getGenericType() { + return getElementClass(); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java new file mode 100644 index 0000000..8c76fb0 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CascadableContainerD.java @@ -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 org.apache.bval.jsr.descriptor; + +import java.lang.reflect.AnnotatedElement; +import java.util.Set; +import java.util.stream.Stream; + +import javax.validation.ValidationException; +import javax.validation.metadata.CascadableDescriptor; +import javax.validation.metadata.ContainerDescriptor; +import javax.validation.metadata.ContainerElementTypeDescriptor; +import javax.validation.metadata.GroupConversionDescriptor; + +import org.apache.bval.jsr.GraphContext; +import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.TypeUtils; + +public abstract class CascadableContainerD<P extends ElementD<?, ?>, E extends AnnotatedElement> extends + ElementD.NonRoot<P, E, MetadataReader.ForContainer<E>> implements CascadableDescriptor, ContainerDescriptor { + + private final boolean cascaded; + private final Set<GroupConversion> groupConversions; + private final Lazy<Set<ContainerElementTypeD>> containerElementTypes; + + protected CascadableContainerD(MetadataReader.ForContainer<E> reader, P parent) { + super(reader, parent); + cascaded = reader.isCascaded(); + groupConversions = reader.getGroupConversions(); + containerElementTypes = new Lazy<>(() -> reader.getContainerElementTypes(this)); + } + + @Override + public Class<?> getElementClass() { + return TypeUtils.getRawType(getGenericType(), parent.getElementClass()); + } + + @Override + public boolean isCascaded() { + return cascaded; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Set<GroupConversionDescriptor> getGroupConversions() { + return (Set) groupConversions; + } + + @Override + public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() { + return containerElementTypes.get().stream().filter(DescriptorManager::isConstrained) + .collect(ToUnmodifiable.set()); + } + + public final Stream<GraphContext> read(GraphContext context) { + Validate.notNull(context); + if (context.getValue() == null) { + return Stream.empty(); + } + try { + return readImpl(context); + } catch (Exception e) { + throw new ValidationException(e); + } + } + + protected Stream<GraphContext> readImpl(GraphContext context) throws Exception { + throw new UnsupportedOperationException(); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java new file mode 100644 index 0000000..6d3b004 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComposedD.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr.descriptor; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + +import javax.validation.metadata.CascadableDescriptor; +import javax.validation.metadata.ConstraintDescriptor; +import javax.validation.metadata.ContainerDescriptor; +import javax.validation.metadata.ContainerElementTypeDescriptor; +import javax.validation.metadata.ElementDescriptor; +import javax.validation.metadata.GroupConversionDescriptor; +import javax.validation.metadata.PropertyDescriptor; + +import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.util.Validate; + +public abstract class ComposedD<D extends ElementD<?, ?>> implements ElementDescriptor { + + static abstract class ForCascadableContainer<D extends CascadableContainerD<?, ?>> extends ComposedD<D> + implements CascadableDescriptor, ContainerDescriptor { + + ForCascadableContainer(List<D> delegates) { + super(delegates); + } + + @Override + public Set<ContainerElementTypeDescriptor> getConstrainedContainerElementTypes() { + return delegates.stream().map(ContainerDescriptor::getConstrainedContainerElementTypes) + .flatMap(Collection::stream).collect(ToUnmodifiable.set()); + } + + @Override + public boolean isCascaded() { + return delegates.stream().anyMatch(CascadableDescriptor::isCascaded); + } + + @Override + public Set<GroupConversionDescriptor> getGroupConversions() { + return delegates.stream().map(CascadableDescriptor::getGroupConversions).flatMap(Collection::stream) + .collect(ToUnmodifiable.set()); + } + } + + static class ForProperty extends ComposedD.ForCascadableContainer<PropertyD<?>> implements PropertyDescriptor { + + ForProperty(List<PropertyD<?>> delegates) { + super(delegates); + } + + @Override + public String getPropertyName() { + return delegates.stream().map(PropertyDescriptor::getPropertyName).findFirst() + .orElseThrow(IllegalStateException::new); + } + } + + public static <T extends ElementD<?, ?>> Stream<T> unwrap(ElementDescriptor descriptor, Class<T> delegateType) { + final Stream<?> s; + + if (descriptor instanceof ComposedD<?>) { + s = ((ComposedD<?>) descriptor).delegates.stream() + // unwrap recursively: + .flatMap(d -> unwrap(d, delegateType)); + } else { + s = Stream.of(descriptor); + } + return s.map(delegateType::cast); + } + + protected final List<D> delegates; + + ComposedD(List<D> delegates) { + super(); + this.delegates = delegates; + + Validate.notNull(delegates, "delegates"); + Validate.isTrue(!delegates.isEmpty(), "At least one delegate is required"); + Validate.isTrue(delegates.stream().noneMatch(Objects::isNull), "null delegates not permitted"); + } + + @Override + public boolean hasConstraints() { + return delegates.stream().anyMatch(ElementDescriptor::hasConstraints); + } + + @Override + public Class<?> getElementClass() { + return delegates.stream().map(ElementDescriptor::getElementClass).findFirst() + .orElseThrow(IllegalStateException::new); + } + + @Override + public Set<ConstraintDescriptor<?>> getConstraintDescriptors() { + return delegates.stream().map(ElementDescriptor::getConstraintDescriptors).flatMap(Collection::stream) + .collect(ToUnmodifiable.set()); + } + + @Override + public ConstraintFinder findConstraints() { + return new Finder(this); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java new file mode 100644 index 0000000..7eeefd2 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ComputeConstraintValidatorClass.java @@ -0,0 +1,183 @@ +package org.apache.bval.jsr.descriptor; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.WildcardType; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.validation.ConstraintDefinitionException; +import javax.validation.ConstraintValidator; +import javax.validation.UnexpectedTypeException; +import javax.validation.constraintvalidation.ValidationTarget; + +import org.apache.bval.jsr.ApacheValidatorFactory; +import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.Reflection; +import org.apache.bval.util.reflection.Reflection.Interfaces; +import org.apache.bval.util.reflection.TypeUtils; +import org.apache.commons.weaver.privilizer.Privilizing; +import org.apache.commons.weaver.privilizer.Privilizing.CallTo; + +@Privilizing(@CallTo(Reflection.class)) +class ComputeConstraintValidatorClass<A extends Annotation> + implements Supplier<Class<? extends ConstraintValidator<A, ?>>> { + + private static class TypeWrapper { + final Class<?> componentType; + final int arrayDepth; + + TypeWrapper(Class<?> type) { + Class<?> c = type; + int d = 0; + while (Object[].class.isAssignableFrom(c)) { + d++; + c = c.getComponentType(); + } + this.componentType = c; + this.arrayDepth = d; + } + + Class<?> unwrap(Class<?> t) { + Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new, + "%s not assignable from %s", t, componentType); + if (arrayDepth == 0) { + return t; + } + return Array.newInstance(t, new int[arrayDepth]).getClass(); + } + } + + private static final String CV = ConstraintValidator.class.getSimpleName(); + private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build(); + + private static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) { + final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class) + .get(ConstraintValidator.class.getTypeParameters()[1]); + Exceptions.raiseUnless(isSupported(result), ConstraintDefinitionException::new, + "Validated type %s declared by %s %s is unsupported", result, CV, validatorType.getName()); + return TypeUtils.getRawType(result, null); + } + + private static boolean isSupported(Type validatedType) { + if (validatedType instanceof Class<?>) { + return true; + } + if (validatedType instanceof ParameterizedType) { + return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments()) + .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED)); + } + return false; + } + + private final ApacheValidatorFactory validatorFactory; + private final Class<?> validatedType; + private final ValidationTarget validationTarget; + private final A constraint; + private final boolean composed; + + ComputeConstraintValidatorClass(ApacheValidatorFactory validatorFactory, ValidationTarget validationTarget, + A constraint, Class<?> validatedType) { + super(); + this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory"); + this.validationTarget = Validate.notNull(validationTarget, "validationTarget"); + this.constraint = Validate.notNull(constraint, "constraint"); + this.validatedType = Validate.notNull(validatedType, "validatedType"); + this.composed = validatorFactory.getAnnotationsManager().isComposed(constraint); + } + + @Override + public Class<? extends ConstraintValidator<A, ?>> get() { + @SuppressWarnings("unchecked") + final Class<A> constraintType = (Class<A>) constraint.annotationType(); + return findValidator(validatorFactory.getConstraintsCache().getConstraintValidatorInfo(constraintType)); + } + + private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) { + switch (validationTarget) { + case PARAMETERS: + return findCrossParameterValidator(infos); + case ANNOTATED_ELEMENT: + return findAnnotatedElementValidator(infos); + default: + return null; + } + } + + private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator( + Set<ConstraintValidatorInfo<A>> infos) { + + final Set<ConstraintValidatorInfo<A>> set = + infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS)) + .collect(Collectors.toSet()); + + @SuppressWarnings("unchecked") + final Class<A> constraintType = (Class<A>) constraint.annotationType(); + + Exceptions.raiseIf(set.size() > 1 || !composed && set.isEmpty(), UnexpectedTypeException::new, + "%d cross-parameter %ss found for constraint type %s", set.size(), CV, constraintType); + + final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType(); + Exceptions.raiseUnless(TypeUtils.isAssignable(Object[].class, getValidatedType(result)), + ConstraintDefinitionException::new, + "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName()); + + return result; + } + + private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator( + Set<ConstraintValidatorInfo<A>> infos) { + + final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = + infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT)) + .map(ConstraintValidatorInfo::getType) + .collect(Collectors.toMap(ComputeConstraintValidatorClass::getValidatedType, Function.identity())); + + final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>(); + + walkHierarchy().filter(validators::containsKey).forEach(type -> { + // if we haven't already found a candidate whose validated type + // is a subtype of the current evaluated type, save: + if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) { + candidates.put(type, validators.get(type)); + } + }); + final String cond; + switch (candidates.size()) { + case 1: + @SuppressWarnings("unchecked") + final Class<? extends ConstraintValidator<A, ?>> result = + (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next(); + return result; + case 0: + if (composed) { + return null; + } + cond = "No compliant"; + break; + default: + cond = "> 1 maximally specific"; + break; + } + throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond, + constraint.annotationType().getName(), CV, TypeUtils.toString(validatedType)); + } + + // account for validated array types by unwrapping and rewrapping component + // type hierarchy: + private Stream<Class<?>> walkHierarchy() { + final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType)); + Stream.Builder<Class<?>> hierarchy = Stream.builder(); + Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy); + return hierarchy.build().map(w::unwrap); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java new file mode 100644 index 0000000..0c1be1b --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstraintD.java @@ -0,0 +1,276 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr.descriptor; + +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import javax.validation.ConstraintDeclarationException; +import javax.validation.ConstraintDefinitionException; +import javax.validation.ConstraintTarget; +import javax.validation.ConstraintValidator; +import javax.validation.Payload; +import javax.validation.ReportAsSingleViolation; +import javax.validation.UnexpectedTypeException; +import javax.validation.ValidationException; +import javax.validation.groups.Default; +import javax.validation.metadata.ConstraintDescriptor; +import javax.validation.metadata.Scope; +import javax.validation.metadata.ValidateUnwrappedValue; +import javax.validation.valueextraction.UnwrapByDefault; +import javax.validation.valueextraction.Unwrapping; +import javax.validation.valueextraction.Unwrapping.Skip; +import javax.validation.valueextraction.Unwrapping.Unwrap; +import javax.validation.valueextraction.ValueExtractor; + +import org.apache.bval.jsr.ApacheValidatorFactory; +import org.apache.bval.jsr.ConstraintAnnotationAttributes; +import org.apache.bval.jsr.ConstraintAnnotationAttributes.Worker; +import org.apache.bval.jsr.metadata.ContainerElementKey; +import org.apache.bval.jsr.metadata.Metas; +import org.apache.bval.jsr.util.AnnotationsManager; +import org.apache.bval.jsr.util.ToUnmodifiable; +import org.apache.bval.jsr.valueextraction.ValueExtractors; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.Validate; +import org.apache.bval.util.reflection.TypeUtils; + +public class ConstraintD<A extends Annotation> implements ConstraintDescriptor<A> { + private static <T> Set<T> set(Supplier<T[]> array) { + return Stream.of(array.get()).collect(ToUnmodifiable.set()); + } + + private final A annotation; + private final Scope scope; + private final Metas<?> meta; + private final Class<?> validatedType; + + private final Lazy<Set<Class<?>>> groups = new Lazy<>(this::computeGroups); + + private final Set<Class<? extends Payload>> payload; + + private final Lazy<Boolean> reportAsSingle = + new Lazy<>(() -> getAnnotation().annotationType().isAnnotationPresent(ReportAsSingleViolation.class)); + + private final Lazy<ValidateUnwrappedValue> valueUnwrapping = new Lazy<>(this::computeValidateUnwrappedValue); + + private final Lazy<Map<String, Object>> attributes; + private final Lazy<Set<ConstraintDescriptor<?>>> composingConstraints; + private final Lazy<List<Class<? extends ConstraintValidator<A, ?>>>> constraintValidatorClasses; + private final Lazy<Class<? extends ConstraintValidator<A, ?>>> constraintValidatorClass; + + public ConstraintD(A annotation, Scope scope, Metas<?> meta, ApacheValidatorFactory validatorFactory) { + this.annotation = Validate.notNull(annotation, "annotation"); + this.scope = Validate.notNull(scope, "scope"); + this.meta = Validate.notNull(meta, "meta"); + this.payload = computePayload(); + this.validatedType = computeValidatedType(validatorFactory); + + attributes = new Lazy<>(() -> AnnotationsManager.readAttributes(annotation)); + + // retain no references to the validatorFactory; only wrap it in lazy + // suppliers + Validate.notNull(validatorFactory, "validatorFactory"); + composingConstraints = new Lazy<>(computeComposingConstraints(validatorFactory)); + constraintValidatorClasses = new Lazy<>(computeConstraintValidatorClasses(validatorFactory)); + + final Supplier<Class<? extends ConstraintValidator<A, ?>>> computeConstraintValidatorClass = + new ComputeConstraintValidatorClass<>(validatorFactory, meta.getValidationTarget(), annotation, + validatedType); + + constraintValidatorClass = new Lazy<>(computeConstraintValidatorClass); + } + + @Override + public A getAnnotation() { + return annotation; + } + + @Override + public Set<Class<?>> getGroups() { + return groups.get(); + } + + @Override + public Set<Class<? extends Payload>> getPayload() { + return payload; + } + + @Override + public List<Class<? extends ConstraintValidator<A, ?>>> getConstraintValidatorClasses() { + return constraintValidatorClasses.get(); + } + + @Override + public Map<String, Object> getAttributes() { + return attributes.get(); + } + + @Override + public Set<ConstraintDescriptor<?>> getComposingConstraints() { + return composingConstraints.get(); + } + + @Override + public boolean isReportAsSingleViolation() { + return reportAsSingle.get().booleanValue(); + } + + @Override + public String getMessageTemplate() { + final boolean required = true; + return read(ConstraintAnnotationAttributes.MESSAGE, required); + } + + @Override + public ConstraintTarget getValidationAppliesTo() { + return read(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO); + } + + @Override + public ValidateUnwrappedValue getValueUnwrapping() { + return valueUnwrapping.get(); + } + + @Override + public <U> U unwrap(Class<U> type) throws ValidationException { + try { + return type.cast(this); + } catch (ClassCastException e) { + throw new ValidationException(e); + } + } + + public Scope getScope() { + return scope; + } + + public Class<?> getDeclaringClass() { + return meta.getDeclaringClass(); + } + + public ElementType getDeclaredOn() { + return meta.getElementType(); + } + + public Class<?> getValidatedType() { + return validatedType; + } + + public Class<? extends ConstraintValidator<A, ?>> getConstraintValidatorClass() { + return constraintValidatorClass.get(); + } + + private <T> T read(ConstraintAnnotationAttributes attr) { + return read(attr, false); + } + + private <T> T read(ConstraintAnnotationAttributes attr, boolean required) { + final Class<? extends Annotation> constraintType = annotation.annotationType(); + final Optional<T> result = + Optional.of(constraintType).map(attr::analyze).filter(Worker::isValid).map(w -> w.<T> read(annotation)); + + Exceptions.raiseIf(required && !result.isPresent(), ConstraintDefinitionException::new, + "Required attribute %s missing from constraint type %s", attr.getAttributeName(), constraintType); + + return result.orElse(null); + } + + private Supplier<Set<ConstraintDescriptor<?>>> computeComposingConstraints( + ApacheValidatorFactory validatorFactory) { + return () -> Stream.of(validatorFactory.getAnnotationsManager().getComposingConstraints(annotation)) + .map(c -> new ConstraintD<>(c, scope, meta, validatorFactory)) + .collect(ToUnmodifiable.set(LinkedHashSet::new)); + } + + @SuppressWarnings("unchecked") + private Supplier<List<Class<? extends ConstraintValidator<A, ?>>>> computeConstraintValidatorClasses( + ApacheValidatorFactory validatorFactory) { + return () -> validatorFactory.getConstraintsCache() + .getConstraintValidatorClasses((Class<A>) annotation.annotationType()); + } + + private ValidateUnwrappedValue computeValidateUnwrappedValue() { + final Set<Class<? extends Payload>> p = getPayload(); + final boolean unwrap = p.contains(Unwrap.class); + final boolean skip = p.contains(Skip.class); + if (unwrap) { + Validate.validState(!skip, "Cannot specify both %s and %s", Unwrap.class.getSimpleName(), + Skip.class.getSimpleName()); + return ValidateUnwrappedValue.UNWRAP; + } + return skip ? ValidateUnwrappedValue.SKIP : ValidateUnwrappedValue.DEFAULT; + } + + private Set<Class<?>> computeGroups() { + final boolean required = true; + final Class<?>[] groups = read(ConstraintAnnotationAttributes.GROUPS, required); + if (groups.length == 0) { + return Collections.singleton(Default.class); + } + return set(() -> groups); + } + + private Set<Class<? extends Payload>> computePayload() { + final boolean required = true; + final Set<Class<? extends Payload>> result = set(() -> read(ConstraintAnnotationAttributes.PAYLOAD, required)); + Exceptions.raiseIf(result.containsAll(Arrays.asList(Unwrapping.Unwrap.class, Unwrapping.Skip.class)), + ConstraintDeclarationException::new, + "Constraint %s declared at %s specifies conflicting value unwrapping hints", annotation, meta.getHost()); + return result; + } + + private Class<?> computeValidatedType(ApacheValidatorFactory validatorFactory) { + final Class<?> rawType = TypeUtils.getRawType(meta.getType(), null); + + Exceptions.raiseIf(rawType == null, UnexpectedTypeException::new, "Could not calculate validated type from %s", + meta.getType()); + + if (payload.contains(Unwrapping.Skip.class)) { + return rawType; + } + final ValueExtractor<?> valueExtractor = + validatorFactory.getValueExtractors().find(new ContainerElementKey(meta.getAnnotatedType(), null)); + + final boolean unwrap = payload.contains(Unwrapping.Unwrap.class); + + if (valueExtractor == null) { + Exceptions.raiseIf(unwrap, ConstraintDeclarationException::new, "No compatible %s found for %s", + ValueExtractor.class.getSimpleName(), meta.getType()); + } else { + @SuppressWarnings("unchecked") + final Class<? extends ValueExtractor<?>> extractorClass = + (Class<? extends ValueExtractor<?>>) valueExtractor.getClass(); + if (unwrap || extractorClass.isAnnotationPresent(UnwrapByDefault.class)) { + return ValueExtractors.getExtractedType(valueExtractor, meta.getType()); + } + } + return rawType; + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java new file mode 100644 index 0000000..b89d29c --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ConstructorD.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr.descriptor; + +import java.lang.reflect.Constructor; + +import javax.validation.metadata.ConstructorDescriptor; + +public class ConstructorD extends ExecutableD<Constructor<?>, MetadataReader.ForConstructor, ConstructorD> + implements ConstructorDescriptor { + + ConstructorD(MetadataReader.ForConstructor reader, BeanD parent) { + super(reader, parent); + } + + @Override + public Class<?> getElementClass() { + return getParent().getElementClass(); + } + + @Override + protected String nameOf(Constructor<?> e) { + return e.getDeclaringClass().getSimpleName(); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java new file mode 100644 index 0000000..7cacff3 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ContainerElementTypeD.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.bval.jsr.descriptor; + +import java.lang.reflect.AnnotatedType; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import javax.validation.ValidationException; +import javax.validation.metadata.ContainerElementTypeDescriptor; +import javax.validation.valueextraction.ValueExtractor; + +import org.apache.bval.jsr.GraphContext; +import org.apache.bval.jsr.metadata.ContainerElementKey; +import org.apache.bval.jsr.util.NodeImpl; +import org.apache.bval.util.Exceptions; +import org.apache.bval.util.Lazy; +import org.apache.bval.util.Validate; + +public class ContainerElementTypeD extends CascadableContainerD<CascadableContainerD<?, ?>, AnnotatedType> + implements ContainerElementTypeDescriptor { + + private static class Receiver implements ValueExtractor.ValueReceiver { + private final GraphContext context; + private Lazy<List<GraphContext>> result = new Lazy<>(ArrayList::new); + + Receiver(GraphContext context) { + super(); + this.context = context; + } + + @Override + public void value(String nodeName, Object object) { + addChild(new NodeImpl.PropertyNodeImpl(nodeName), object); + } + + @Override + public void iterableValue(String nodeName, Object object) { + final NodeImpl.PropertyNodeImpl node = new NodeImpl.PropertyNodeImpl(nodeName); + node.setInIterable(true); + addChild(node, object); + } + + @Override + public void indexedValue(String nodeName, int i, Object object) { + final NodeImpl.PropertyNodeImpl node = new NodeImpl.PropertyNodeImpl(nodeName); + node.setIndex(Integer.valueOf(i)); + addChild(node, object); + } + + @Override + public void keyedValue(String nodeName, Object key, Object object) { + final NodeImpl.PropertyNodeImpl node = new NodeImpl.PropertyNodeImpl(nodeName); + node.setKey(key); + addChild(node, object); + } + + private void addChild(NodeImpl node, Object value) { + result.get().add(context.child(node, value)); + } + } + + private final ContainerElementKey key; + + ContainerElementTypeD(ContainerElementKey key, MetadataReader.ForContainer<AnnotatedType> reader, + CascadableContainerD<?, ?> parent) { + super(reader, parent); + this.key = Validate.notNull(key, "key"); + } + + @Override + public Class<?> getContainerClass() { + return key.getContainerClass(); + } + + @Override + public Integer getTypeArgumentIndex() { + return Integer.valueOf(key.getTypeArgumentIndex()); + } + + public ContainerElementKey getKey() { + return key; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + protected Stream<GraphContext> readImpl(GraphContext context) throws Exception { + final ValueExtractor valueExtractor = context.getValidatorContext().getValueExtractors().find(key); + Exceptions.raiseIf(valueExtractor == null, ValidationException::new, "No %s found for %s", + ValueExtractor.class.getSimpleName(), key); + + final Receiver receiver = new Receiver(context); + try { + valueExtractor.extractValues(context.getValue(), receiver); + } catch (ValidationException e) { + throw e; + } catch (Exception e) { + throw new ValidationException(e); + } + return receiver.result.get().stream(); + } +} http://git-wip-us.apache.org/repos/asf/bval/blob/3f287a7a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java ---------------------------------------------------------------------- diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java new file mode 100644 index 0000000..0d51800 --- /dev/null +++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/CrossParameterD.java @@ -0,0 +1,18 @@ +package org.apache.bval.jsr.descriptor; + +import java.lang.reflect.Executable; + +import javax.validation.metadata.CrossParameterDescriptor; + +public class CrossParameterD<P extends ExecutableD<?, ?, P>, E extends Executable> + extends ElementD.NonRoot<P, E, MetadataReader.ForElement<E, ?>> implements CrossParameterDescriptor { + + protected CrossParameterD(MetadataReader.ForElement<E, ?> reader, P parent) { + super(reader, parent); + } + + @Override + public Class<?> getElementClass() { + return Object[].class; + } +}
