You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bval.apache.org by mb...@apache.org on 2010/10/07 23:46:56 UTC
svn commit: r1005645 - in /incubator/bval/sandbox/lang3-work:
bval-jsr303/src/main/java/org/apache/bval/jsr303/
bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/
bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/
Author: mbenson
Date: Thu Oct 7 21:46:56 2010
New Revision: 1005645
URL: http://svn.apache.org/viewvc?rev=1005645&view=rev
Log:
add StubConstraint fluent interface
Added:
incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java (with props)
incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java (with props)
Modified:
incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
Modified: incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java?rev=1005645&r1=1005644&r2=1005645&view=diff
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java (original)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java Thu Oct 7 21:46:56 2010
@@ -199,4 +199,17 @@ public enum ConstraintAnnotationAttribut
constraint));
}
}
+
+ /**
+ * Get the default value of this attribute on the given annotation type.
+ * @param <T>
+ * @param type
+ * @return Object
+ */
+ public <T> T getDefaultValue(Class<? extends Annotation> type) {
+ validateOn(type);
+ @SuppressWarnings("unchecked")
+ T result = (T) SecureActions.getMethod(type, getAttributeName()).getDefaultValue();
+ return result;
+ }
}
Added: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java?rev=1005645&view=auto
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java (added)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java Thu Oct 7 21:46:56 2010
@@ -0,0 +1,267 @@
+/*
+ * 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.jsr303.dynamic;
+
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.GROUPS;
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.MESSAGE;
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.PAYLOAD;
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.VALUE;
+
+import java.lang.annotation.Annotation;
+import java.security.PrivilegedAction;
+import java.util.EnumMap;
+import java.util.Map;
+
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.util.PrivilegedActions;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+import org.apache.commons.proxy2.stub.AnnotationConfigurer;
+import org.apache.commons.proxy2.stub.AnnotationFactory;
+import org.apache.commons.proxy2.stub.StubConfigurer;
+
+/**
+ * Fluent interface for building constraints.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StubConstraint {
+ /**
+ * Builder class that handles common constraint attributes.
+ */
+ public static class Builder {
+
+ private EnumMap<ConstraintAnnotationAttributes, Object> attributes =
+ new EnumMap<ConstraintAnnotationAttributes, Object>(ConstraintAnnotationAttributes.class);
+
+ private class Config<A extends Annotation> extends AnnotationConfigurer<A> {
+ /**
+ * Create a new StubConstraint.Builder.Config instance.
+ */
+ protected Config() {
+ super();
+ validateConstraintType(getStubType());
+ }
+
+ private Config(Class<A> type) {
+ super(type);
+ validateConstraintType(getStubType());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void configure(final A stub) {
+ for (final Map.Entry<ConstraintAnnotationAttributes, Object> e : StubConstraint.Builder.this.attributes
+ .entrySet()) {
+ when(getAttribute(e.getKey().getAttributeName(), getStubType(), stub)).thenReturn(e.getValue());
+ }
+
+ }
+
+ private A build() {
+ return AnnotationFactory.INSTANCE.create(this);
+ }
+ }
+
+ /**
+ * Custom constraint configurer.
+ *
+ * @param <A>
+ */
+ public abstract class CustomConfig<A extends Annotation> extends Config<A> {
+
+ /**
+ * {@inheritDoc}
+ */
+ protected final void configure(A stub) {
+ super.configure(stub);
+ customConfigure(stub);
+ }
+
+ /**
+ * Perform configuration specific to the constraint type.
+ *
+ * @param stub
+ */
+ protected abstract void customConfigure(A stub);
+
+ /**
+ * Build the constraint annotation.
+ *
+ * @return A
+ */
+ public A build() {
+ return super.build();
+ }
+ }
+
+ private Builder() {
+ }
+
+ /**
+ * Set the groups that will be set on built constraints.
+ *
+ * @param groups
+ * @return this, per fluent/chained idiom
+ */
+ public StubConstraint.Builder withGroups(Class<?>... groups) {
+ attributes.put(GROUPS, groups);
+ return this;
+ }
+
+ /**
+ * Set the message that will be set on built constraints.
+ *
+ * @param message
+ * @return this, per fluent/chained idiom
+ */
+ public StubConstraint.Builder withMessage(String message) {
+ attributes.put(MESSAGE, message);
+ return this;
+ }
+
+ /**
+ * Set the payload that will be set on built constraints.
+ *
+ * @param payload
+ * @return this, per fluent/chained idiom
+ */
+ public StubConstraint.Builder withPayload(Class<? extends Payload>... payload) {
+ attributes.put(PAYLOAD, payload);
+ return this;
+ }
+
+ /**
+ * Get a ConstraintConfig stub configurer for the specified type, with default or no custom attributes.
+ *
+ * @param <A>
+ * @param type
+ * @return ConstraintConfig<A>
+ */
+ public <A extends Annotation> A build(Class<A> type) {
+ return new Config<A>(type).build();
+ }
+ }
+
+ private static final Builder DEFAULT_BUILDER = new Builder();
+
+ private StubConstraint() {
+ }
+
+ /**
+ * Get a builder for refining built constraints.
+ *
+ * @return StubConstraint.Builder instance
+ */
+ public static StubConstraint.Builder build() {
+ return new StubConstraint.Builder();
+ }
+
+ /**
+ * Build a default constraint annotation of type
+ *
+ * @param <A>
+ * @param type
+ * @return
+ */
+ public static <A extends Annotation> A build(Class<A> type) {
+ return DEFAULT_BUILDER.build(type);
+ }
+
+ /**
+ * Build a multivalued annotation.
+ *
+ * @param <A>
+ * @param <M>
+ * @param type
+ * @param value
+ * @return L
+ */
+ public static <A extends Annotation, M extends Annotation> M buildMultiple(final Class<M> type, final A... value) {
+ /*
+ * Here we go
+ */
+ if (!PrivilegedActions.run(new PrivilegedAction<Boolean>() {
+
+ public Boolean run() {
+ try {
+ return TypeUtils.isInstance(value, type.getMethod(VALUE.getAttributeName()).getGenericReturnType());
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ })) {
+ throw new ValidationException(String.format("%2$s does not seem to be a valid value for %1$s()", type,
+ value));
+ }
+
+ if (value != null) {
+ @SuppressWarnings("unchecked")
+ Class<? extends Annotation> constraintType =
+ (Class<? extends Annotation>) value.getClass().getComponentType();
+ validateConstraintType(constraintType);
+ }
+
+ return AnnotationFactory.INSTANCE.create(new StubConfigurer<M>(type) {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void configure(M stub) {
+ when(getAttribute(VALUE.getAttributeName(), type, stub)).thenReturn(value);
+ }
+ });
+ }
+
+ private static <A extends Annotation> void validateConstraintType(Class<A> type) {
+ for (ConstraintAnnotationAttributes a : ArrayUtils.toArray(MESSAGE, GROUPS, PAYLOAD)) {
+ a.validateOn(type);
+ }
+ }
+
+ /**
+ * Reimplement the retrieval of attribute values using an externally specified type; incomplete stubbed annotations
+ * cannot answer {@link Annotation#annotationType()}.
+ *
+ * @param attributeName
+ * @param type
+ * @param instance
+ * @return Object
+ */
+ private static <T> T getAttribute(final String attributeName, final Class<? extends Annotation> type,
+ final Object instance) {
+ return PrivilegedActions.run(new PrivilegedAction<T>() {
+
+ @SuppressWarnings("unchecked")
+ public T run() {
+ try {
+ return (T) type.getMethod(attributeName).invoke(instance);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+}
Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/main/java/org/apache/bval/jsr303/dynamic/StubConstraint.java
------------------------------------------------------------------------------
svn:keywords = Revision Date
Added: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java
URL: http://svn.apache.org/viewvc/incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java?rev=1005645&view=auto
==============================================================================
--- incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java (added)
+++ incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java Thu Oct 7 21:46:56 2010
@@ -0,0 +1,173 @@
+/*
+ * 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.jsr303.dynamic;
+
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.GROUPS;
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.MESSAGE;
+import static org.apache.bval.jsr303.ConstraintAnnotationAttributes.PAYLOAD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.util.Arrays;
+
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Null;
+import javax.validation.constraints.Size;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.proxy2.stub.AnnotationFactory;
+import org.junit.Test;
+
+/**
+ * Test/example of {@link StubConstraint} use.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StubConstraintTest {
+
+ public interface Small {
+ }
+
+ public interface Large {
+ }
+
+ public static class CustomPayload implements Payload {
+ }
+
+ @Test
+ public void testDefault() throws Exception {
+ Null defaultNull = StubConstraint.build(Null.class);
+ assertEquals(MESSAGE.getDefaultValue(Null.class), defaultNull.message());
+ assertTrue(Arrays.equals((Class<?>[]) GROUPS.getDefaultValue(Null.class), defaultNull.groups()));
+ assertTrue(Arrays.equals((Class<?>[]) PAYLOAD.getDefaultValue(Null.class), defaultNull.payload()));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testCommonAttributes() throws Exception {
+ Null stubbedNull =
+ StubConstraint.build().withGroups(Small.class, Large.class).withMessage("gotta be null")
+ .withPayload(CustomPayload.class).build(Null.class);
+ assertEquals("gotta be null", stubbedNull.message());
+ assertTrue(Arrays.equals(ArrayUtils.<Class<?>> toArray(Small.class, Large.class), stubbedNull.groups()));
+ assertTrue(Arrays.equals(ArrayUtils.<Class<? extends Payload>> toArray(CustomPayload.class),
+ stubbedNull.payload()));
+ }
+
+ @Test
+ public void testCustomConfig() throws Exception {
+ Size size = StubConstraint.build().new CustomConfig<Size>() {
+ @Override
+ protected void customConfigure(Size stub) {
+ when(stub.min()).thenReturn(5).when(stub.max()).thenReturn(50);
+ }
+ }.build();
+
+ assertEquals(MESSAGE.getDefaultValue(Size.class), size.message());
+ assertTrue(Arrays.equals((Class<?>[]) GROUPS.getDefaultValue(Size.class), size.groups()));
+ assertTrue(Arrays.equals((Class<?>[]) PAYLOAD.getDefaultValue(Size.class), size.payload()));
+ assertEquals(5, size.min());
+ assertEquals(50, size.max());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testCommonAttributesWithCustomConfig() throws Exception {
+ Size size =
+ StubConstraint.build().withGroups(Large.class, Small.class).withMessage("size between 5 and 50")
+ .withPayload(CustomPayload.class).new CustomConfig<Size>() {
+ @Override
+ protected void customConfigure(Size stub) {
+ when(stub.min()).thenReturn(5).when(stub.max()).thenReturn(50);
+ }
+ }.build();
+
+ assertEquals("size between 5 and 50", size.message());
+ assertTrue(Arrays.equals(ArrayUtils.<Class<?>> toArray(Large.class, Small.class), size.groups()));
+ assertTrue(Arrays.equals(ArrayUtils.<Class<? extends Payload>> toArray(CustomPayload.class), size.payload()));
+ assertEquals(5, size.min());
+ assertEquals(50, size.max());
+ }
+
+ @Test(expected = ValidationException.class)
+ public void testInvalidAnnotationType() throws Exception {
+ StubConstraint.build(Retention.class);
+ }
+
+ @Test
+ public void testMultiple() throws Exception {
+ Size.List sizes =
+ StubConstraint.buildMultiple(Size.List.class,
+ StubConstraint.build().withGroups(Small.class).withMessage("1-5").new CustomConfig<Size>() {
+
+ @Override
+ protected void customConfigure(Size stub) {
+ when(stub.min()).thenReturn(1).when(stub.max()).thenReturn(5);
+ }
+ }.build(), StubConstraint.build().withGroups(Large.class).withMessage("1-50").new CustomConfig<Size>() {
+
+ @Override
+ protected void customConfigure(Size stub) {
+ when(stub.min()).thenReturn(1).when(stub.max()).thenReturn(50);
+ }
+ }.build());
+
+ assertEquals(2, sizes.value().length);
+ Size smallSize = sizes.value()[0];
+ assertEquals("1-5", smallSize.message());
+ assertTrue(Arrays.equals(ArrayUtils.<Class<?>> toArray(Small.class), smallSize.groups()));
+ assertTrue(Arrays.equals((Class<?>[]) PAYLOAD.getDefaultValue(Size.class), smallSize.payload()));
+ assertEquals(1, smallSize.min());
+ assertEquals(5, smallSize.max());
+ Size largeSize = sizes.value()[1];
+ assertEquals("1-50", largeSize.message());
+ assertTrue(Arrays.equals(ArrayUtils.<Class<?>> toArray(Large.class), largeSize.groups()));
+ assertTrue(Arrays.equals((Class<?>[]) PAYLOAD.getDefaultValue(Size.class), largeSize.payload()));
+ assertEquals(1, largeSize.min());
+ assertEquals(50, largeSize.max());
+ }
+
+ @Test
+ public void testEmptyMultiple() throws Exception {
+ Null.List nullList = StubConstraint.<Null, Null.List> buildMultiple(Null.List.class);
+ assertTrue(Arrays.equals(new Null[] { }, nullList.value()));
+ }
+
+ @Test(expected = ValidationException.class)
+ public void testMultipleWithInvalidType() throws Exception {
+ StubConstraint.buildMultiple(Retention.class, StubConstraint.build(NotNull.class));
+ }
+
+ @Test(expected = ValidationException.class)
+ public void testMultipleWithInvalidSingleType() throws Exception {
+ StubConstraint.buildMultiple(Retention.class, AnnotationFactory.INSTANCE.create(Documented.class));
+ }
+
+ @Test(expected = ValidationException.class)
+ public void testEmptyMultipleWithInvalidSingleType() throws Exception {
+ StubConstraint.<NotNull, Null.List> buildMultiple(Null.List.class);
+ }
+
+ @Test(expected = ValidationException.class)
+ public void testMismatchedSingleAndMultiple() throws Exception {
+ StubConstraint.buildMultiple(Null.List.class, StubConstraint.build(NotNull.class));
+ }
+}
Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/bval/sandbox/lang3-work/bval-jsr303d/src/test/java/org/apache/bval/jsr303/dynamic/StubConstraintTest.java
------------------------------------------------------------------------------
svn:keywords = Revision Date