Skip to content

Commit

Permalink
HV-371: Establishing AggregatedMethodMetaData (work in progress)
Browse files Browse the repository at this point in the history
  • Loading branch information
gunnarmorling committed Mar 4, 2011
1 parent 8dc6142 commit a245c6f
Show file tree
Hide file tree
Showing 5 changed files with 350 additions and 0 deletions.
@@ -0,0 +1,133 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.hibernate.validator.metadata;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.hibernate.validator.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.util.CollectionHelper.newHashMap;

/**
* An aggregated view of the constraint related meta data for a given method and
* all the methods in the inheritance hierarchy which it overrides or
* implements.
*
* @author Gunnar Morling
*/
public class AggregatedMethodMetaData implements Iterable<BeanMetaConstraint<?, ? extends Annotation>> {

private final Method method;

private final Map<Class<?>, MethodMetaData> metaDataByDefiningType = newHashMap();

public AggregatedMethodMetaData(MethodMetaData metaData) {

this.method = metaData.getMethod();
metaDataByDefiningType.put( metaData.getMethod().getDeclaringClass(), metaData );
}

public Method getMethod() {
return method;
}

public void addMetaData(MethodMetaData metaData) {
metaDataByDefiningType.put( metaData.getMethod().getDeclaringClass(), metaData );
}

/**
* Whether a cascaded validation of the return value of the represented
* method shall be performed or not. This is the case if either the method
* itself is annotated with {@link Valid} or any of the method's up in the
* inheritance hierarchy which it overrides.
*
* @return <code>True</code>, if a cascaded return value validation shall be
* performed, <code>false</code> otherwise.
*/
public boolean isCascading() {

for ( MethodMetaData oneMethodMetaData : metaDataByDefiningType.values() ) {
if ( oneMethodMetaData.isCascading() ) {
return true;
}
}

return false;
}

public boolean isConstrained() {

for ( MethodMetaData oneMethodMetaData : metaDataByDefiningType.values() ) {
if ( oneMethodMetaData.isConstrained() ) {
return true;
}
}

return false;
}

/**
* An iterator with the return value constraints of the represented method.
*/
public Iterator<BeanMetaConstraint<?, ? extends Annotation>> iterator() {

List<BeanMetaConstraint<?, ? extends Annotation>> theValue = newArrayList();

for ( MethodMetaData oneMethodMetaData : metaDataByDefiningType.values() ) {
for ( BeanMetaConstraint<?, ? extends Annotation> oneConstraint : oneMethodMetaData ) {
theValue.add( oneConstraint );
}
}

return theValue.iterator();
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( method == null ) ? 0 : method.hashCode() );
return result;
}

@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
AggregatedMethodMetaData other = (AggregatedMethodMetaData) obj;
if ( method == null ) {
if ( other.method != null ) {
return false;
}
}
else if ( !method.equals( other.method ) ) {
return false;
}
return true;
}

}
Expand Up @@ -95,6 +95,8 @@ public final class BeanMetaDataImpl<T> implements BeanMetaData<T> {
*/
private Map<Class<?>, Map<Method, MethodMetaData>> methodMetaConstraints = newHashMap();

private Map<Method, AggregatedMethodMetaData> aggregatedMethodMetaData = newHashMap();

/**
* Contains meta data for all method's of this type (including the method's
* from its super types). Used only at construction time to determine whether
Expand Down Expand Up @@ -354,6 +356,10 @@ public Map<Class<?>, MethodMetaData> getMetaDataForMethod(Method method) {
return theValue;
}

public AggregatedMethodMetaData getAggregatedMethodMetaData(Method method) {
return aggregatedMethodMetaData.get( method );
}

public PropertyDescriptor getPropertyDescriptor(String property) {
return propertyDescriptors.get( property );
}
Expand Down Expand Up @@ -450,6 +456,8 @@ private void addMetaConstraint(Class<?> clazz, BeanMetaConstraint<T, ? extends A
private void addMethodMetaConstraint(Class<?> clazz, MethodMetaData methodMetaData) {
allMethods.add( methodMetaData );

addToAggregation( methodMetaData );

Map<Method, MethodMetaData> constraintsOfClass = methodMetaConstraints.get( clazz );

if ( constraintsOfClass == null ) {
Expand All @@ -474,6 +482,22 @@ private void addMethodMetaConstraint(Class<?> clazz, MethodMetaData methodMetaDa
}
}

private void addToAggregation(MethodMetaData metaData) {

Method method = metaData.getMethod();

for ( AggregatedMethodMetaData oneAggregatedMetaData : aggregatedMethodMetaData.values() ) {

if ( ReflectionHelper.haveSameSignature( method, oneAggregatedMetaData.getMethod() ) ) {
oneAggregatedMetaData.addMetaData( metaData );
return;
}
}

AggregatedMethodMetaData newMetaData = new AggregatedMethodMetaData( metaData );
aggregatedMethodMetaData.put( method, newMetaData );
}

private void addCascadedMember(Member member) {
ReflectionHelper.setAccessibility( member );
cascadedMembers.add( member );
Expand Down
@@ -0,0 +1,140 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.hibernate.validator.test.metadata;

import java.lang.reflect.Method;
import javax.validation.constraints.NotNull;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import org.hibernate.validator.metadata.AggregatedMethodMetaData;
import org.hibernate.validator.metadata.BeanMetaDataCache;
import org.hibernate.validator.metadata.BeanMetaDataImpl;
import org.hibernate.validator.metadata.ConstraintHelper;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

/**
* Tests creation of {@link AggregatedMethodMetaData} in {@link BeanMetaDataImpl}.
*
* @author Gunnar Morling
*/
public class AggregatedMethodMetaDataTest {

private BeanMetaDataImpl<CustomerRepositoryExt> beanMetaData;

@BeforeMethod
public void setupBeanMetaData() {

beanMetaData = new BeanMetaDataImpl<CustomerRepositoryExt>(
CustomerRepositoryExt.class, new ConstraintHelper(), new BeanMetaDataCache()
);
}

@Test
public void methodWithConstrainedParameter() throws Exception {

Method method = CustomerRepositoryExt.class.getMethod( "createCustomer", String.class, String.class );
AggregatedMethodMetaData methodMetaData = beanMetaData.getAggregatedMethodMetaData( method );

assertEquals( methodMetaData.getMethod(), method );
assertFalse( methodMetaData.isCascading() );
assertTrue( methodMetaData.isConstrained() );
assertSize( methodMetaData, 0 );
}

@Test
public void methodWithCascadedParameter() throws Exception {

Method method = CustomerRepositoryExt.class.getMethod( "saveCustomer", Customer.class );
AggregatedMethodMetaData methodMetaData = beanMetaData.getAggregatedMethodMetaData( method );

assertEquals( methodMetaData.getMethod(), method );
assertFalse( methodMetaData.isCascading() );
assertTrue( methodMetaData.isConstrained() );
assertSize( methodMetaData, 0 );
}

@Test
public void methodWithConstrainedReturnValue() throws Exception {

Method method = CustomerRepositoryExt.class.getMethod( "bar" );
AggregatedMethodMetaData methodMetaData = beanMetaData.getAggregatedMethodMetaData( method );

assertEquals( methodMetaData.getMethod(), method );
assertFalse( methodMetaData.isCascading() );
assertTrue( methodMetaData.isConstrained() );
assertSize( methodMetaData, 1 );
assertEquals(
methodMetaData.iterator().next().getDescriptor().getAnnotation().annotationType(), NotNull.class
);
}

@Test
public void returnValueConstraintsAddUpInHierarchy() throws Exception {

Method method = CustomerRepositoryExt.class.getMethod( "baz" );
AggregatedMethodMetaData methodMetaData = beanMetaData.getAggregatedMethodMetaData( method );

assertEquals( methodMetaData.getMethod(), method );
assertFalse( methodMetaData.isCascading() );
assertTrue( methodMetaData.isConstrained() );
assertSize( methodMetaData, 2 );
}

@Test
public void methodWithCascadedReturnValue() throws Exception {

Method method = CustomerRepositoryExt.class.getMethod( "foo" );
AggregatedMethodMetaData methodMetaData = beanMetaData.getAggregatedMethodMetaData( method );

assertEquals( methodMetaData.getMethod(), method );
assertTrue( methodMetaData.isCascading() );
assertTrue( methodMetaData.isConstrained() );
assertSize( methodMetaData, 0 );
}

@Test
public void unconstrainedMethod() throws Exception {

Method method = CustomerRepositoryExt.class.getMethod( "updateCustomer", Customer.class );
AggregatedMethodMetaData methodMetaData = beanMetaData.getAggregatedMethodMetaData( method );

assertEquals( methodMetaData.getMethod(), method );
assertFalse( methodMetaData.isCascading() );
assertFalse( methodMetaData.isConstrained() );
assertSize( methodMetaData, 0 );
}

/**
* TODO GM: invoke on TestUtil, once it is merged.
*/
@Deprecated
private void assertSize(Iterable<?> iterable, int expectedCount) {

int i = 0;

for ( @SuppressWarnings("unused") Object o : iterable ) {
i++;
}

assertEquals( i, expectedCount, "Actual size of iterable [" + iterable + "] differed from expected size." );
}
}
Expand Up @@ -47,4 +47,8 @@ public Customer bar() {
return null;
}

@NotNull
public int baz() {
return 0;
}
}
@@ -0,0 +1,49 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and/or its affiliates, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed 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.hibernate.validator.test.metadata;

import javax.validation.constraints.Min;


/**
* @author Gunnar Morling
*/
public class CustomerRepositoryExt extends CustomerRepository {

public Customer createCustomer(String firstName, String lastName) {
return null;
}

public void saveCustomer(Customer customer) {
}

public void updateCustomer(Customer customer) {
}

public Customer foo() {
return null;
}

public Customer bar() {
return null;
}

@Min(0)
public int baz() {
return 0;
}
}

0 comments on commit a245c6f

Please sign in to comment.