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