diff options
Diffstat (limited to 'src')
6 files changed, 894 insertions, 0 deletions
| diff --git a/src/main/java/com/amazon/carbonado/constraint/ConstraintDefinition.java b/src/main/java/com/amazon/carbonado/constraint/ConstraintDefinition.java new file mode 100644 index 0000000..22514fc --- /dev/null +++ b/src/main/java/com/amazon/carbonado/constraint/ConstraintDefinition.java @@ -0,0 +1,111 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * 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 com.amazon.carbonado.constraint;
 +
 +import java.lang.annotation.*;
 +
 +/**
 + * Allows annotations to be defined that restrict property values. The
 + * annotation is just a pointer to a constraint checking class. If the
 + * constraint class is not explicitly provided, it defaults to a static inner
 + * class named "Constraint" in the annotation itself.
 + *
 + * <p>The constraint class must have a public constructor that accepts the
 + * annotation that has the ConstraintDefinition annotation. It must also define
 + * several "constrain" methods which perform constraint checks on specific
 + * property types.
 + * <p>
 + * Example integer constraint:
 + * <pre>
 + * @Documented
 + * <b>@Retention(RetentionPolicy.RUNTIME)</b>
 + * <b>@Target(ElementType.METHOD)</b>
 + * <b>@ConstraintDefinition</b>
 + * public @interface IntegerConstraint {
 + *     int min() default Integer.MIN_VALUE;
 + *
 + *     int max() default Integer.MAX_VALUE;
 + *
 + *     public static class Constraint {
 + *         private final String propertyName;
 + *         private final int min;
 + *         private final int max;
 + *
 + *         // Constructor may throw a MalformedTypeException if
 + *         // params supplied by annotation are illegal.
 + *
 + *         /**
 + *          * @param type optional type of object that contains the constrained property
 + *          * @param propertyName name of property with constraint
 + *          * @param annotation specific annotation that binds to this constraint class
 + *          */
 + *         public Constraint(Class type, String propertyName, IntegerConstraint annotation) {
 + *             this.propertyName = propertyName;
 + *             this.min = annotation.min();
 + *             this.max = annotation.max();
 + *         }
 + *
 + *         // Define a constrain method for each supported property type.
 + *
 + *         /**
 + *          * @param propertyValue specific value to constrain
 + *          */
 + *         public void constrain(int propertyValue) throws IllegalArgumentException {
 + *             if (propertyValue < min || propertyValue > max) {
 + *                 throw new IllegalArgumentException
 + *                     ("Value for \"" + propertyName + "\" must be in range " +
 + *                      min + ".." + max + ": " + propertyValue);
 + *             }
 + *         }
 + *     }
 + * }
 + * </pre>
 + *
 + * The newly defined integer constraint can be applied to property mutators.
 + *
 + * <pre>
 + * public interface UserInfo extends Storable {
 + *     ...
 + *
 + *     // Constraint is called before setting age.
 + *     <b>@IntegerConstraint(min=0, max=120)</b>
 + *     void setAge(int value);
 + * }
 + * </pre>
 + *
 + * @author Brian S O'Neill
 + */
 +@Documented
 +@Retention(RetentionPolicy.RUNTIME)
 +@Target(ElementType.ANNOTATION_TYPE)
 +public @interface ConstraintDefinition {
 +    /**
 +     * Specify class which will perform constraint checking. Must have a public
 +     * constructor with the signature
 +     * <code>(Class type, String propertyName, <i>Annotation</i>)</code>,
 +     * where <code><i>Annotation</i></code> refers to the annotation with the
 +     * constraint definition.
 +     *
 +     * <p>The implementation class need not be explicitly specified. By
 +     * default, the constraint class must be a static inner class of the
 +     * annotation, named "Constraint".
 +     */
 +    // Use void.class to mean default.
 +    Class implementation() default void.class;
 +}
 diff --git a/src/main/java/com/amazon/carbonado/constraint/FloatConstraint.java b/src/main/java/com/amazon/carbonado/constraint/FloatConstraint.java new file mode 100644 index 0000000..31b6503 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/constraint/FloatConstraint.java @@ -0,0 +1,201 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * 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 com.amazon.carbonado.constraint;
 +
 +import java.lang.annotation.*;
 +import java.util.Arrays;
 +
 +import com.amazon.carbonado.MalformedTypeException;
 +
 +/**
 + * Limits the value of a property to be a member of a specific set. The
 + * property value may be a boxed or unboxed float, double, String,
 + * CharSequence, char, Character, or character array. If the property value is
 + * outside the set, an IllegalArgumentException is thrown.
 + *
 + * <p>Example:<pre>
 + * public interface PolarCoordinate extends Storable {
 + *     double getTheta();
 + *
 + *     <b>@FloatConstraint(min=0, max=Math.PI * 2, disallowed=Double.NaN)</b>
 + *     void setTheta(double radians);
 + *
 + *     ...
 + * }
 + * </pre>
 + *
 + * @author Brian S O'Neill
 + * @see IntegerConstraint
 + * @see TextConstraint
 + */
 +@Documented
 +@Retention(RetentionPolicy.RUNTIME)
 +@Target(ElementType.METHOD)
 +@ConstraintDefinition
 +public @interface FloatConstraint {
 +    /**
 +     * Specific allowed values for property. Default is unlimited.
 +     */
 +    double[] allowed() default {};
 +
 +    /**
 +     * Specific disallowed values for property. Default is none.
 +     */
 +    double[] disallowed() default {};
 +
 +    /**
 +     * Specify minimum allowed value for float/double property. Default is unlimited.
 +     */
 +    double min() default Double.MIN_VALUE;
 +
 +    /**
 +     * Specify maximum allowed value for float/double property. Default is unlimited.
 +     */
 +    double max() default Double.MAX_VALUE;
 +
 +    /**
 +     * Constraint implementation for {@link FloatConstraint}.
 +     */
 +    public static class Constraint {
 +        private final String mPropertyName;
 +        private final double mMinValue;
 +        private final double mMaxValue;
 +
 +        /** Disallowed values, sorted for binary search. */
 +        private final double[] mDisallowed;
 +
 +        /** Allowed values, sorted for binary search. */
 +        private final double[] mAllowed;
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param ann specific annotation that binds to this constraint class
 +         */
 +        public Constraint(Class<?> type, String propertyName, FloatConstraint ann) {
 +            this(type, propertyName,
 +                 ann.min(), ann.max(), ann.allowed(), ann.disallowed());
 +        }
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param min minimum allowed value
 +         * @param max maximum allowed value
 +         * @param allowed optional set of allowed values
 +         * @param disallowed optional set of disallowed values
 +         */
 +        public Constraint(Class<?> type, String propertyName,
 +                          double min, double max, double[] allowed, double[] disallowed) {
 +            mPropertyName = propertyName;
 +            mMinValue = min;
 +            mMaxValue = max;
 +            if (mMaxValue < mMinValue) {
 +                throw new MalformedTypeException
 +                    (type, "Illegal range for float constraint on property \"" +
 +                     propertyName + "\": " + rangeString());
 +            }
 +
 +            if (disallowed == null || disallowed.length == 0) {
 +                disallowed = null;
 +            } else {
 +                disallowed = disallowed.clone();
 +                Arrays.sort(disallowed);
 +            }
 +
 +            if (allowed == null || allowed.length == 0) {
 +                allowed = null;
 +            } else {
 +                allowed = allowed.clone();
 +                Arrays.sort(allowed);
 +                for (double value : allowed) {
 +                    if (value < mMinValue || value > mMaxValue ||
 +                        (disallowed != null && Arrays.binarySearch(disallowed, value) >= 0)) {
 +                        throw new MalformedTypeException
 +                            (type, "Allowed value contradiction for float constraint " +
 +                             "on property \"" + propertyName + "\": " + value);
 +                    }
 +                }
 +
 +                // No need to have a set of disallowed values.
 +                disallowed = null;
 +            }
 +
 +            mDisallowed = disallowed;
 +            mAllowed = allowed;
 +        }
 +
 +        public void constrain(double propertyValue) {
 +            if (propertyValue < mMinValue || propertyValue > mMaxValue) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" must be in range " +
 +                     rangeString() + ": " + propertyValue);
 +            }
 +            if (mDisallowed != null && Arrays.binarySearch(mDisallowed, propertyValue) >= 0) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is disallowed: " + propertyValue);
 +            }
 +            if (mAllowed != null && Arrays.binarySearch(mAllowed, propertyValue) < 0) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is not allowed: " + propertyValue);
 +            }
 +        }
 +
 +        public void constrain(CharSequence propertyValue) {
 +            if (propertyValue != null) {
 +                try {
 +                    constrain(Double.parseDouble(propertyValue.toString()));
 +                } catch (NumberFormatException e) {
 +                    throw new IllegalArgumentException
 +                        ("Value for \"" + mPropertyName + "\" is not a number: " +
 +                         propertyValue);
 +                }
 +            }
 +        }
 +
 +        public void constrain(char propertyValue) {
 +            if ('0' <= propertyValue && propertyValue <= '9') {
 +                constrain((double) (propertyValue - '0'));
 +            } else {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is not a number: " + propertyValue);
 +            }
 +        }
 +
 +        public void constrain(char[] propertyValue) {
 +            if (propertyValue != null) {
 +                constrain(new String(propertyValue));
 +            }
 +        }
 +
 +        private String rangeString() {
 +            StringBuilder b = new StringBuilder();
 +            b.append('(');
 +            if (mMinValue > Double.MIN_VALUE) {
 +                b.append(mMinValue);
 +            }
 +            b.append("..");
 +            if (mMaxValue < Double.MAX_VALUE) {
 +                b.append(mMaxValue);
 +            }
 +            b.append(')');
 +            return b.toString();
 +        }
 +    }
 +}
 diff --git a/src/main/java/com/amazon/carbonado/constraint/IntegerConstraint.java b/src/main/java/com/amazon/carbonado/constraint/IntegerConstraint.java new file mode 100644 index 0000000..69fa5cc --- /dev/null +++ b/src/main/java/com/amazon/carbonado/constraint/IntegerConstraint.java @@ -0,0 +1,233 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * 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 com.amazon.carbonado.constraint;
 +
 +import java.lang.annotation.*;
 +import java.util.Arrays;
 +
 +import com.amazon.carbonado.MalformedTypeException;
 +
 +/**
 + * Limits the value of a property to be a member of a specific set. The
 + * property value may be a boxed or unboxed byte, short, int, long, float,
 + * double, String, CharSequence, char, Character, or character array. If the
 + * property value is outside the set, an IllegalArgumentException is thrown.
 + *
 + * <p>Example:<pre>
 + * public interface UserInfo extends Storable {
 + *     int getAge();
 + *
 + *     <b>@IntegerConstraint(min=0, max=120)</b>
 + *     void setAge(int value);
 + *
 + *     int getRoleID();
 + *
 + *     <b>@IntegerConstraint(allowed={ROLE_REGULAR, ROLE_ADMIN})</b>
 + *     void setRoleID(int role);
 + *
 + *     ...
 + * }
 + * </pre>
 + *
 + * @author Brian S O'Neill
 + * @see FloatConstraint
 + * @see TextConstraint
 + */
 +@Documented
 +@Retention(RetentionPolicy.RUNTIME)
 +@Target(ElementType.METHOD)
 +@ConstraintDefinition
 +public @interface IntegerConstraint {
 +    /**
 +     * Specific allowed values for property. Default is unlimited.
 +     */
 +    long[] allowed() default {};
 +
 +    /**
 +     * Specific disallowed values for property. Default is none.
 +     */
 +    long[] disallowed() default {};
 +
 +    /**
 +     * Specify minimum allowed value for integer property. Default is unlimited.
 +     */
 +    long min() default Long.MIN_VALUE;
 +
 +    /**
 +     * Specify maximum allowed value for integer property. Default is unlimited.
 +     */
 +    long max() default Long.MAX_VALUE;
 +
 +    /**
 +     * Constraint implementation for {@link IntegerConstraint}.
 +     */
 +    public static class Constraint {
 +        private final String mPropertyName;
 +
 +        private final long mMinValue;
 +        private final long mMaxValue;
 +
 +        /** Disallowed values, sorted for binary search. */
 +        private final long[] mDisallowed;
 +
 +        /** Allowed values, sorted for binary search. */
 +        private final long[] mAllowed;
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param ann specific annotation that binds to this constraint class
 +         */
 +        public Constraint(Class<?> type, String propertyName, IntegerConstraint ann) {
 +            this(type, propertyName,
 +                 ann.min(), ann.max(), ann.allowed(), ann.disallowed());
 +        }
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param min minimum allowed value
 +         * @param max maximum allowed value
 +         * @param allowed optional set of allowed values
 +         * @param disallowed optional set of disallowed values
 +         */
 +        public Constraint(Class<?> type, String propertyName,
 +                          long min, long max, long[] allowed, long[] disallowed) {
 +            mPropertyName = propertyName;
 +            mMinValue = min;
 +            mMaxValue = max;
 +            if (mMaxValue < mMinValue) {
 +                throw new MalformedTypeException
 +                    (type, "Illegal range for integer constraint on property \"" +
 +                     propertyName + "\": " + rangeString());
 +            }
 +
 +            if (disallowed == null || disallowed.length == 0) {
 +                disallowed = null;
 +            } else {
 +                disallowed = disallowed.clone();
 +                Arrays.sort(disallowed);
 +            }
 +
 +            if (allowed == null || allowed.length == 0) {
 +                allowed = null;
 +            } else {
 +                allowed = allowed.clone();
 +                Arrays.sort(allowed);
 +                for (long value : allowed) {
 +                    if (value < mMinValue || value > mMaxValue ||
 +                        (disallowed != null && Arrays.binarySearch(disallowed, value) >= 0)) {
 +                        throw new MalformedTypeException
 +                            (type, "Allowed value contradiction for integer constraint " +
 +                             "on property \"" + propertyName + "\": " + value);
 +                    }
 +                }
 +
 +                // No need to have a set of disallowed values.
 +                disallowed = null;
 +            }
 +
 +            mDisallowed = disallowed;
 +            mAllowed = allowed;
 +        }
 +
 +        public void constrain(long propertyValue) {
 +            if (propertyValue < mMinValue || propertyValue > mMaxValue) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" must be in range " +
 +                     rangeString() + ": " + propertyValue);
 +            }
 +            if (mDisallowed != null && Arrays.binarySearch(mDisallowed, propertyValue) >= 0) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is disallowed: " + propertyValue);
 +            }
 +            if (mAllowed != null && Arrays.binarySearch(mAllowed, propertyValue) < 0) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is not allowed: " + propertyValue);
 +            }
 +        }
 +
 +        public void constrain(double propertyValue) {
 +            if (propertyValue < mMinValue || propertyValue > mMaxValue) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" must be in range " +
 +                     rangeString() + ": " + propertyValue);
 +            }
 +            if (mDisallowed != null) {
 +                long longValue = (long) propertyValue;
 +                if (longValue == propertyValue &&
 +                    Arrays.binarySearch(mDisallowed, longValue) >= 0) {
 +
 +                    throw new IllegalArgumentException
 +                        ("Value for \"" + mPropertyName + "\" is disallowed: " + propertyValue);
 +                }
 +            }
 +            if (mAllowed != null) {
 +                long longValue = (long) propertyValue;
 +                if (longValue != propertyValue ||
 +                    Arrays.binarySearch(mAllowed, longValue) < 0) {
 +
 +                    throw new IllegalArgumentException
 +                        ("Value for \"" + mPropertyName + "\" is not allowed: " + propertyValue);
 +                }
 +            }
 +        }
 +
 +        public void constrain(CharSequence propertyValue) {
 +            if (propertyValue != null) {
 +                try {
 +                    constrain(Long.parseLong(propertyValue.toString()));
 +                } catch (NumberFormatException e) {
 +                    throw new IllegalArgumentException
 +                        ("Value for \"" + mPropertyName + "\" is not an integer: " +
 +                         propertyValue);
 +                }
 +            }
 +        }
 +
 +        public void constrain(char propertyValue) {
 +            if ('0' <= propertyValue && propertyValue <= '9') {
 +                constrain((long) (propertyValue - '0'));
 +            } else {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is not an integer: " + propertyValue);
 +            }
 +        }
 +
 +        public void constrain(char[] propertyValue) {
 +            if (propertyValue != null) {
 +                constrain(new String(propertyValue));
 +        }
 +        }
 +
 +        private String rangeString() {
 +            StringBuilder b = new StringBuilder();
 +            b.append('(');
 +            if (mMinValue > Long.MIN_VALUE) {
 +                b.append(mMinValue);
 +            }
 +            b.append("..");
 +            if (mMaxValue < Long.MAX_VALUE) {
 +                b.append(mMaxValue);
 +            }
 +            b.append(')');
 +            return b.toString();
 +        }
 +    }
 +}
 diff --git a/src/main/java/com/amazon/carbonado/constraint/LengthConstraint.java b/src/main/java/com/amazon/carbonado/constraint/LengthConstraint.java new file mode 100644 index 0000000..7e17a1f --- /dev/null +++ b/src/main/java/com/amazon/carbonado/constraint/LengthConstraint.java @@ -0,0 +1,173 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * 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 com.amazon.carbonado.constraint;
 +
 +import java.lang.annotation.*;
 +
 +import com.amazon.carbonado.MalformedTypeException;
 +
 +/**
 + * Limits the value of a property to lie within a specific length range. The
 + * property value may be a String, CharSequence, or any kind of array. If the
 + * set property size is outside the range, an IllegalArgumentException is
 + * thrown.
 + *
 + * <p>Example:<pre>
 + * public interface UserInfo extends Storable {
 + *     int getFirstName();
 + *
 + *     <b>@LengthConstraint(min=1, max=50)</b>
 + *     void setFirstName(String name);
 + *
 + *     ...
 + * }
 + * </pre>
 + *
 + * @author Brian S O'Neill
 + */
 +@Documented
 +@Retention(RetentionPolicy.RUNTIME)
 +@Target(ElementType.METHOD)
 +@ConstraintDefinition
 +public @interface LengthConstraint {
 +    /**
 +     * Specify minimum allowed size for property. Default is zero.
 +     */
 +    int min() default 0;
 +
 +    /**
 +     * Specify maximum allowed value for property. Default is unlimited.
 +     */
 +    int max() default Integer.MAX_VALUE;
 +
 +    /**
 +     * Constraint implementation for {@link LengthConstraint}.
 +     */
 +    public static class Constraint {
 +        private final String mPropertyName;
 +        private final int mMinLength;
 +        private final int mMaxLength;
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param ann specific annotation that binds to this constraint class
 +         */
 +        public Constraint(Class<?> type, String propertyName, LengthConstraint ann) {
 +            this(type, propertyName, ann.min(), ann.max());
 +        }
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param min minimum allowed size
 +         * @param max maximum allowed size
 +         */
 +        public Constraint(Class<?> type, String propertyName, int min, int max) {
 +            mPropertyName = propertyName;
 +            mMinLength = min;
 +            mMaxLength = max;
 +            if (mMinLength < 0 || mMaxLength < mMinLength) {
 +                throw new MalformedTypeException
 +                    (type, "Illegal length constraint for property \"" + propertyName +
 +                     "\": " + rangeString());
 +            }
 +        }
 +
 +        public void constrain(CharSequence str) {
 +            if (str != null) {
 +                constrainLength(str.length());
 +            }
 +        }
 +
 +        public void constrain(boolean[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(byte[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(short[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(char[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(int[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(long[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(float[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(double[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        public void constrain(Object[] array) {
 +            if (array != null) {
 +                constrainLength(array.length);
 +            }
 +        }
 +
 +        private void constrainLength(int length) {
 +            if (length < mMinLength || length > mMaxLength) {
 +                throw new IllegalArgumentException
 +                    ("Value length for \"" + mPropertyName + "\" must be in range " +
 +                     rangeString() + ": " + length);
 +            }
 +        }
 +
 +        private String rangeString() {
 +            StringBuilder b = new StringBuilder();
 +            b.append('(');
 +            b.append(mMinLength);
 +            b.append("..");
 +            if (mMaxLength < Integer.MAX_VALUE) {
 +                b.append(mMaxLength);
 +            }
 +            b.append(')');
 +            return b.toString();
 +        }
 +    }
 +}
 diff --git a/src/main/java/com/amazon/carbonado/constraint/TextConstraint.java b/src/main/java/com/amazon/carbonado/constraint/TextConstraint.java new file mode 100644 index 0000000..08574e8 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/constraint/TextConstraint.java @@ -0,0 +1,152 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * 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 com.amazon.carbonado.constraint;
 +
 +import java.lang.annotation.*;
 +import java.util.Arrays;
 +
 +import com.amazon.carbonado.MalformedTypeException;
 +
 +/**
 + * Limits the value of a property to be a member of a specific set. The
 + * property value may be a String, CharSequence, char, Character, or character
 + * array. If the property value is outside the set, an IllegalArgumentException
 + * is thrown.
 + *
 + * <p>Example:<pre>
 + * public interface UserInfo extends Storable {
 + *     char isActive();
 + *
 + *     <b>@TextConstraint(allowed={"Y", "N"})</b>
 + *     void setActive(char value);
 + *
 + *     ...
 + * }
 + * </pre>
 + *
 + * @author Brian S O'Neill
 + * @see IntegerConstraint
 + * @see FloatConstraint
 + */
 +@Documented
 +@Retention(RetentionPolicy.RUNTIME)
 +@Target(ElementType.METHOD)
 +@ConstraintDefinition
 +public @interface TextConstraint {
 +    /**
 +     * Specific allowed values for property. Default is unlimited.
 +     */
 +    String[] allowed() default {};
 +
 +    /**
 +     * Specific disallowed values for property. Default is none.
 +     */
 +    String[] disallowed() default {};
 +
 +    /**
 +     * Constraint implementation for {@link TextConstraint}.
 +     */
 +    public static class Constraint {
 +        private final String mPropertyName;
 +
 +        /** Allowed values, sorted for binary search. */
 +        private final String[] mAllowed;
 +
 +        /** Disallowed values, sorted for binary search. */
 +        private final String[] mDisallowed;
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param ann specific annotation that binds to this constraint class
 +         */
 +        public Constraint(Class<?> type, String propertyName, TextConstraint ann) {
 +            this(type, propertyName, ann.allowed(), ann.disallowed());
 +        }
 +
 +        /**
 +         * @param type type of object that contains the constrained property
 +         * @param propertyName name of property with constraint
 +         * @param allowed optional set of allowed values
 +         * @param disallowed optional set of disallowed values
 +         */
 +        public Constraint(Class<?> type, String propertyName,
 +                          String[] allowed, String[] disallowed) {
 +            mPropertyName = propertyName;
 +
 +            if (disallowed == null || disallowed.length == 0) {
 +                disallowed = null;
 +            } else {
 +                disallowed = disallowed.clone();
 +                Arrays.sort(disallowed);
 +            }
 +
 +            if (allowed == null || allowed.length == 0) {
 +                allowed = null;
 +            } else {
 +                allowed = allowed.clone();
 +                Arrays.sort(allowed);
 +                if (disallowed != null) {
 +                    for (String value : allowed) {
 +                        if (Arrays.binarySearch(disallowed, value) >= 0) {
 +                            throw new MalformedTypeException
 +                                (type, "Allowed value contradiction for text constraint " +
 +                                 "on property \"" + propertyName + "\": " + value);
 +                        }
 +                    }
 +
 +                    // No need to have a set of disallowed values.
 +                    disallowed = null;
 +                }
 +            }
 +
 +            mDisallowed = disallowed;
 +            mAllowed = allowed;
 +        }
 +
 +        public void constrain(CharSequence propertyValue) {
 +            constrain(propertyValue.toString());
 +        }
 +
 +        public void constrain(String propertyValue) {
 +            if (propertyValue == null) {
 +                return;
 +            }
 +            if (mDisallowed != null && Arrays.binarySearch(mDisallowed, propertyValue) >= 0) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is disallowed: " + propertyValue);
 +            }
 +            if (mAllowed != null && Arrays.binarySearch(mAllowed, propertyValue) < 0) {
 +                throw new IllegalArgumentException
 +                    ("Value for \"" + mPropertyName + "\" is not allowed: " + propertyValue);
 +            }
 +        }
 +
 +        public void constrain(char propertyValue) {
 +            constrain(Character.toString(propertyValue));
 +        }
 +
 +        public void constrain(char[] propertyValue) {
 +            if (propertyValue != null) {
 +                constrain(new String(propertyValue));
 +            }
 +        }
 +    }
 +}
 +
 diff --git a/src/main/java/com/amazon/carbonado/constraint/package-info.java b/src/main/java/com/amazon/carbonado/constraint/package-info.java new file mode 100644 index 0000000..517c27e --- /dev/null +++ b/src/main/java/com/amazon/carbonado/constraint/package-info.java @@ -0,0 +1,24 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * 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.
 + */
 +
 +/**
 + * Contains annotations and implementations for supporting property constraints.
 + *
 + * @see com.amazon.carbonado.constraint.ConstraintDefinition
 + */
 +package com.amazon.carbonado.constraint;
 | 
