org.apache.bval.jsr.metadata.ReflectionBuilder Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.metadata;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.validation.ConstraintDeclarationException;
import javax.validation.ConstraintTarget;
import javax.validation.GroupSequence;
import javax.validation.ParameterNameProvider;
import javax.validation.Valid;
import javax.validation.constraintvalidation.ValidationTarget;
import javax.validation.groups.ConvertGroup;
import org.apache.bval.jsr.ApacheValidatorFactory;
import org.apache.bval.jsr.ConstraintAnnotationAttributes;
import org.apache.bval.jsr.groups.GroupConversion;
import org.apache.bval.jsr.util.AnnotationsManager;
import org.apache.bval.jsr.util.Methods;
import org.apache.bval.jsr.util.ToUnmodifiable;
import org.apache.bval.util.Exceptions;
import org.apache.bval.util.Lazy;
import org.apache.bval.util.ObjectUtils;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;
import org.apache.commons.weaver.privilizer.Privilizing;
import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
@Privilizing(@CallTo(Reflection.class))
public class ReflectionBuilder {
private class ForBean implements MetadataBuilder.ForBean {
private final Meta> meta;
ForBean(Meta> meta) {
super();
this.meta = Validate.notNull(meta, "meta");
}
@Override
public MetadataBuilder.ForClass getClass(Meta> ignored) {
return new ReflectionBuilder.ForClass<>(meta);
}
@Override
public Map> getFields(Meta> ignored) {
final Field[] declaredFields = Reflection.getDeclaredFields(meta.getHost());
if (declaredFields.length == 0) {
return Collections.emptyMap();
}
return Stream.of(declaredFields).filter(f -> !(Modifier.isStatic(f.getModifiers()) || f.isSynthetic()))
.collect(
Collectors.toMap(Field::getName, f -> new ReflectionBuilder.ForContainer<>(new Meta.ForField(f))));
}
@Override
public Map> getGetters(Meta> ignored) {
final Method[] declaredMethods = Reflection.getDeclaredMethods(meta.getHost());
if (declaredMethods.length == 0) {
return Collections.emptyMap();
}
final Map> getters = new HashMap<>();
for (Method m : declaredMethods) {
if (Methods.isGetter(m)) {
getters.computeIfAbsent(Methods.propertyName(m), k -> new LinkedHashSet<>()).add(m);
}
}
final Map> result = new TreeMap<>();
getters.forEach((k, methods) -> {
if ("class".equals(k)) {
return;
}
final List> delegates = methods.stream()
.map(g -> new ReflectionBuilder.ForContainer<>(new Meta.ForMethod(g))).collect(Collectors.toList());
if (delegates.isEmpty()) {
return;
}
final MetadataBuilder.ForContainer builder;
if (delegates.size() == 1) {
builder = delegates.get(0);
} else {
builder = compositeBuilder.get().new ForContainer<>(delegates);
}
result.put(k, builder);
});
return result;
}
@Override
public Map>> getConstructors(Meta> ignored) {
final Constructor extends T>[] declaredConstructors = Reflection.getDeclaredConstructors(meta.getHost());
if (declaredConstructors.length == 0) {
return Collections.emptyMap();
}
return Stream.of(declaredConstructors).collect(
Collectors.toMap(Signature::of, c -> new ReflectionBuilder.ForExecutable<>(new Meta.ForConstructor<>(c),
ParameterNameProvider::getParameterNames)));
}
@Override
public Map> getMethods(Meta> ignored) {
final Method[] declaredMethods = Reflection.getDeclaredMethods(meta.getHost());
if (declaredMethods.length == 0) {
return Collections.emptyMap();
}
final Map> methodsBySignature = new HashMap<>();
for (Method m : declaredMethods) {
if (!Modifier.isStatic(m.getModifiers())) {
methodsBySignature.computeIfAbsent(Signature.of(m), k -> new LinkedHashSet<>()).add(m);
}
}
final Map> result = new TreeMap<>();
// we can't filter the getters since they can be validated, todo: read the config to know if we need or not
methodsBySignature.forEach((sig, methods) -> {
final List> delegates =
methods.stream().map(g -> new ReflectionBuilder.ForExecutable<>(new Meta.ForMethod(g),
ParameterNameProvider::getParameterNames)).collect(Collectors.toList());
if (delegates.isEmpty()) {
return;
}
final MetadataBuilder.ForExecutable builder;
if (delegates.size() == 1) {
builder = delegates.get(0);
} else {
builder = compositeBuilder.get().new ForExecutable, Method>(
delegates, ParameterNameProvider::getParameterNames);
}
result.put(sig, builder);
});
return result;
}
}
private abstract class ForElement implements MetadataBuilder.ForElement {
final Meta meta;
ForElement(Meta meta) {
super();
this.meta = Validate.notNull(meta, "meta");
}
@Override
public Annotation[] getDeclaredConstraints(Meta ignored) {
return AnnotationsManager.getDeclaredConstraints(meta);
}
@Override
public boolean equals(Object obj) {
return obj == this || this.getClass().isInstance(obj) && ((ForElement>) obj).meta.equals(meta);
}
@Override
public int hashCode() {
return Objects.hash(getClass(), meta);
}
}
private class ForClass extends ForElement> implements MetadataBuilder.ForClass {
ForClass(Meta> meta) {
super(meta);
}
@Override
public List> getGroupSequence(Meta> ignored) {
final GroupSequence groupSequence = AnnotationsManager.getAnnotation(meta.getHost(), GroupSequence.class);
return groupSequence == null ? null : Collections.unmodifiableList(Arrays.asList(groupSequence.value()));
}
}
private class ForContainer extends ReflectionBuilder.ForElement
implements MetadataBuilder.ForContainer {
ForContainer(Meta meta) {
super(meta);
}
@Override
public Map> getContainerElementTypes(
Meta ignored) {
final AnnotatedType annotatedType = meta.getAnnotatedType();
if (annotatedType instanceof AnnotatedParameterizedType) {
final AnnotatedParameterizedType container = (AnnotatedParameterizedType) annotatedType;
final Map> result = new TreeMap<>();
final AnnotatedType[] typeArgs = container.getAnnotatedActualTypeArguments();
for (int i = 0; i < typeArgs.length; i++) {
final ContainerElementKey key = new ContainerElementKey(container, i);
result.put(key, new ReflectionBuilder.ForContainer<>(new Meta.ForContainerElement(meta, key)));
}
return result;
}
return Collections.emptyMap();
}
@Override
public boolean isCascade(Meta ignored) {
return AnnotationsManager.isAnnotationDirectlyPresent(meta.getHost(), Valid.class);
}
@Override
public Set getGroupConversions(Meta ignored) {
return Stream.of(AnnotationsManager.getDeclaredAnnotationsByType(meta.getHost(), ConvertGroup.class))
.map(cg -> GroupConversion.from(cg.from()).to(cg.to())).collect(ToUnmodifiable.set());
}
}
private class ForExecutable implements MetadataBuilder.ForExecutable {
final Meta meta;
final BiFunction> getParameterNames;
ForExecutable(Meta meta, BiFunction> getParameterNames) {
super();
this.meta = Validate.notNull(meta, "meta");
this.getParameterNames = Validate.notNull(getParameterNames, "getParameterNames");
}
@Override
public List> getParameters(Meta ignored) {
final Parameter[] parameters = meta.getHost().getParameters();
if (parameters.length == 0) {
return Collections.emptyList();
}
final List parameterNames = getParameterNames.apply(validatorFactory.getParameterNameProvider(),meta.getHost());
return IntStream.range(0, parameters.length).mapToObj(
n -> new ReflectionBuilder.ForContainer<>(new Meta.ForParameter(parameters[n], parameterNames.get(n))))
.collect(ToUnmodifiable.list());
}
@Override
public ForContainer getReturnValue(Meta ignored) {
return new ReflectionBuilder.ForContainer(meta) {
@Override
public Annotation[] getDeclaredConstraints(Meta meta) {
return getConstraints(ConstraintTarget.RETURN_VALUE);
}
};
}
@Override
public MetadataBuilder.ForElement getCrossParameter(Meta ignored) {
return new ReflectionBuilder.ForElement(meta) {
@Override
public Annotation[] getDeclaredConstraints(Meta meta) {
return getConstraints(ConstraintTarget.PARAMETERS);
}
};
}
private Annotation[] getConstraints(ConstraintTarget constraintTarget) {
return Optional.of(getConstraintsByTarget()).map(m -> m.get(constraintTarget))
.map(l -> l.toArray(new Annotation[l.size()])).orElse(ObjectUtils.EMPTY_ANNOTATION_ARRAY);
}
private Map> getConstraintsByTarget() {
final Annotation[] declaredConstraints = AnnotationsManager.getDeclaredConstraints(meta);
if (ObjectUtils.isEmptyArray(declaredConstraints)) {
return Collections.emptyMap();
}
final Map> result = new EnumMap<>(ConstraintTarget.class);
for (Annotation constraint : declaredConstraints) {
final Class extends Annotation> constraintType = constraint.annotationType();
final Optional explicitTarget =
Optional.of(ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.analyze(constraintType))
.filter(ConstraintAnnotationAttributes.Worker::isValid)
. map(w -> w.read(constraint)).filter(et -> et != ConstraintTarget.IMPLICIT);
final ConstraintTarget target;
if (explicitTarget.isPresent()) {
target = explicitTarget.get();
} else {
final Set supportedTargets =
validatorFactory.getAnnotationsManager().supportedTargets(constraintType);
if (supportedTargets.size() == 1) {
final ValidationTarget validationTarget = supportedTargets.iterator().next();
switch (validationTarget) {
case PARAMETERS:
target = ConstraintTarget.PARAMETERS;
break;
case ANNOTATED_ELEMENT:
target = ConstraintTarget.RETURN_VALUE;
break;
default:
throw Exceptions.create(IllegalStateException::new, "Unknown %s %s for %s",
ValidationTarget.class.getSimpleName(), validationTarget, constraintType);
}
} else {
target = impliedConstraintTarget();
if (target == null) {
Exceptions.raise(ConstraintDeclarationException::new,
"Found %d possible %s types for constraint type %s and no explicit assignment via #%s()",
supportedTargets.size(), ValidationTarget.class.getSimpleName(),
constraintType.getName(),
ConstraintAnnotationAttributes.VALIDATION_APPLIES_TO.getAttributeName());
}
}
}
result.computeIfAbsent(target, k -> new ArrayList<>()).add(constraint);
}
return result;
}
private ConstraintTarget impliedConstraintTarget() {
if (meta.getHost().getParameterCount() == 0) {
return ConstraintTarget.RETURN_VALUE;
}
if (Void.TYPE.equals(meta.getType())) {
return ConstraintTarget.PARAMETERS;
}
return null;
}
}
private final ApacheValidatorFactory validatorFactory;
private final Lazy compositeBuilder;
public ReflectionBuilder(ApacheValidatorFactory validatorFactory) {
super();
this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
this.compositeBuilder =
new Lazy<>(() -> new CompositeBuilder(this.validatorFactory, x -> AnnotationBehavior.ABSTAIN));
}
public MetadataBuilder.ForBean forBean(Class beanClass) {
return new ReflectionBuilder.ForBean<>(new Meta.ForClass(beanClass));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy