blob: 2791a258d535524532b37f6a69a299742d579b82 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF 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.ConstraintValidatorFactory;
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<br/>
*/
final class GroupValidationContextImpl<T> extends BeanValidationContext<ConstraintValidationListener<T>> implements
GroupValidationContext<T>, MessageInterpolator.Context {
private final MessageInterpolator messageResolver;
private final PathImpl path;
private final MetaBean rootMetaBean;
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<ConstraintValidatorIdentity> validatedConstraints = new HashSet<ConstraintValidatorIdentity>();
private ConstraintValidation<?> constraintValidation;
private final TraversableResolver traversableResolver;
private final ConstraintValidatorFactory constraintValidatorFactory;
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<T> listener, MessageInterpolator aMessageResolver,
TraversableResolver traversableResolver, ParameterNameProvider parameterNameProvider,
ConstraintValidatorFactory constraintValidatorFactory,
MetaBean rootMetaBean) {
// inherited variable 'validatedObjects' is of type:
// HashMap<GraphBeanIdentity, Set<PathImpl>> in this class
super(listener, new HashMap<GraphBeanIdentity, Set<PathImpl>>());
this.messageResolver = aMessageResolver;
this.constraintValidatorFactory = constraintValidatorFactory;
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<PathImpl> validatedPathsForGBI = (Set<PathImpl>) validatedObjects.get(gbi);
if (validatedPathsForGBI == null) {
validatedPathsForGBI = new HashSet<PathImpl>();
validatedObjects.put(gbi, validatedPathsForGBI);
}
// If any of the paths is a subpath of the current path, there is a
// circular dependency, so return false
for (PathImpl validatedPath : validatedPathsForGBI) {
if (path.isSubPathOf(validatedPath)) {
return false;
}
}
// Else, add the currentPath to the set of validatedPaths
validatedPathsForGBI.add(PathImpl.copy(path));
return true;
}
/**
* {@inheritDoc}
*/
public boolean collectValidated(ConstraintValidator<?, ?> constraint) {
ConstraintValidatorIdentity cvi = new ConstraintValidatorIdentity(getBean(), getPropertyPath(), constraint);
return this.validatedConstraints.add(cvi);
}
/**
* Reset the validated constraints.
*/
public void resetValidatedConstraints() {
validatedConstraints.clear();
}
/**
* {@inheritDoc} If an associated object is validated, add the association
* field or JavaBeans property name and a dot ('.') as a prefix to the
* previous rules. uses prop[index] in property path for elements in
* to-many-relationships.
*
* @return the path in dot notation
*/
public PathImpl getPropertyPath() {
PathImpl currentPath = PathImpl.copy(path);
if (getMetaProperty() != null) {
currentPath.addProperty(getMetaProperty().getName());
}
return currentPath;
}
/**
* {@inheritDoc}
*/
public MetaBean getRootMetaBean() {
return rootMetaBean;
}
/**
* Set the Groups.
*
* @param groups
*/
public void setGroups(Groups groups) {
this.groups = groups;
}
/**
* {@inheritDoc}
*/
public Groups getGroups() {
return groups;
}
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> T unwrap(Class<T> 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;
}
@Override
public ConstraintValidatorFactory getConstraintValidatorFactory() {
return constraintValidatorFactory;
}
/**
* {@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;
}
}