Author: kentam
Date: Thu Jan 27 09:39:56 2005
New Revision: 126631

URL: http://svn.apache.org/viewcvs?view=rev&rev=126631
Log:
Submitting a control property constraint validator.  This validator will ensure 
values assigned to 
control properties satisfy the constraints defined on the properties.  The 
constraints that the 
validator check include property type constraints (e.g. min/max values for an 
Int property type) as 
well as property set constraints (e.g. membership rules).  

Since values can be assigned to a control property declaratively (via 
annotations) and programmatically
(via property setters), the validator will be called upon both at build time as 
well as runtime. 


Contributor: Hoi Lam



Added:
   
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/AnnotationConstraintValidator.java
   (contents, props changed)
   
incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControl.java
   (contents, props changed)
   
incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControlImpl.jcs
   (contents, props changed)
   
incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropertyConstraint.java
   (contents, props changed)
   
incubator/beehive/trunk/controls/test/webapps/controlsWeb/property/property_constraints/
   
incubator/beehive/trunk/controls/test/webapps/controlsWeb/property/property_constraints/Controller.jpf
   (contents, props changed)
Modified:
   
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java
   
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java
   
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java
   
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java
   
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java
   
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties
   
incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java

Modified: 
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java&r1=126630&p2=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java
    (original)
+++ 
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/bean/AnnotationMemberTypes.java
    Thu Jan 27 09:39:56 2005
@@ -63,6 +63,7 @@
     public final static long   OPTIONAL_LONG   = Long.MIN_VALUE;
     public final static short  OPTIONAL_SHORT  = Short.MIN_VALUE;
     public final static char   OPTIONAL_CHAR   = Character.MIN_VALUE;
+    public final static byte   OPTIONAL_BYTE   = Byte.MIN_VALUE;
 
     /**
      * Marks a member as optional.  Member must have

Modified: 
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java&r1=126630&p2=incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java
        (original)
+++ 
incubator/beehive/trunk/controls/src/api/org/apache/beehive/controls/api/properties/PropertyKey.java
        Thu Jan 27 09:39:56 2005
@@ -113,6 +113,7 @@
     public Class<? extends Annotation>  getPropertySet()  { return  
_propertySet; }
     public String   getPropertyName() { return _propertyName; }
     public Class    getPropertyType() { return _propertyType; }
+    public Annotation[] getAnnotations() { return _getMethod.getAnnotations();}
 
     Class<? extends Annotation>  _propertySet;
     String  _propertyName;

Added: 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/AnnotationConstraintValidator.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/AnnotationConstraintValidator.java?view=auto&rev=126631
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/AnnotationConstraintValidator.java
    Thu Jan 27 09:39:56 2005
@@ -0,0 +1,668 @@
+package org.apache.beehive.controls.runtime.bean;
+
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * 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.
+ *
+ * $Header:$
+ */
+
+import java.io.File;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.beehive.controls.api.bean.AnnotationMemberTypes;
+import 
org.apache.beehive.controls.api.bean.AnnotationConstraints.MembershipRule;
+import 
org.apache.beehive.controls.api.bean.AnnotationConstraints.MembershipRuleValues;
+import org.apache.beehive.controls.api.properties.PropertyKey;
+import org.apache.beehive.controls.api.properties.PropertySet;
+
+import com.sun.mirror.declaration.AnnotationMirror;
+import com.sun.mirror.declaration.AnnotationTypeDeclaration;
+import com.sun.mirror.declaration.AnnotationTypeElementDeclaration;
+import com.sun.mirror.declaration.AnnotationValue;
+import com.sun.mirror.declaration.Declaration;
+import com.sun.mirror.declaration.MethodDeclaration;
+import com.sun.mirror.declaration.TypeDeclaration;
+import com.sun.mirror.type.AnnotationType;
+
+/**
+ * This class offers methods for validating values assigned to a control 
property.
+ * The validation process will ensure
+ *    1. The value is appropriate for the property's property type
+ *    2. The value satisfies the constraints defined on the property type
+ *    3. The value satisfies the constraints defined on the property set that 
the property is defined in.
+ * Refer to [EMAIL PROTECTED] 
org.apache.beehive.controls.api.bean.AnnotationMemberTypes 
AnnotationMemberTypes} and 
+ * [EMAIL PROTECTED] 
org.apache.beehive.controls.api.bean.AnnotationConstraints 
AnnotationConstraints} for more 
+ * information on property constraints.
+ */
+public class AnnotationConstraintValidator
+{
+
+    public AnnotationConstraintValidator()
+    {
+        super();
+    }
+
+    /**
+     * This method ensures that any control property value assignment satisfies
+     * all property constraints. This method should be called by control
+     * property setters to ensure values assigned to properties at runtime are
+     * validated.
+     * 
+     * @param key
+     *            The property that the specified key is assigned to
+     * @param value
+     *            The value assigned to the specified property key
+     * @throws IllegalArgumentException
+     *             when the value assigned to the specified property key does
+     *             not satisfy a property constraint.
+     */
+    public static void validate(PropertyKey key, Object value)
+            throws IllegalArgumentException
+    {
+        validate(key.getAnnotations(), value);
+    }
+
+    /**
+     * This method ensures that any control property value assignment satisfies
+     * all property constraints. This method should be called from an 
annotation
+     * processor to ensure declarative control property assignment using
+     * annotations are validated at build time. This method is currently called
+     * from ControlAnnotationProcessor and ControlClientAnnotationProcessor.
+     * 
+     * @param d
+     *            a declaration which may contain a control property value
+     *            assignment
+     * @throws IllegalArgumentException
+     *             when the declaration contains a control property value
+     *             assignment that does not satisfy a property constraint.
+     */
+    public static void validate(Declaration d) throws IllegalArgumentException
+    {
+        Collection<AnnotationMirror> mirrors = d.getAnnotationMirrors();
+
+        // for each annotations defined on the declaration, if the annotation
+        // is a PropertySet, ensure the values assigned to the properties
+        // satisfy all PropertySet and PropertyType constraints.
+        for (AnnotationMirror m : mirrors)
+        {
+            AnnotationType type = m.getAnnotationType();
+            AnnotationTypeDeclaration decl = 
m.getAnnotationType().getDeclaration();
+            if (decl.getAnnotation(PropertySet.class) != null)
+            {
+                Iterator<Map.Entry<AnnotationTypeElementDeclaration, 
AnnotationValue>> i = m
+                        .getElementValues().entrySet().iterator();
+                while (i.hasNext())
+                {
+                    Map.Entry<AnnotationTypeElementDeclaration, 
AnnotationValue> entry = i
+                            .next();
+                    Collection<Annotation> annotations = 
getMemberTypeAnnotations(entry.getKey());
+                    if (annotations.size() > 0)
+                    {
+                        Annotation[] anArray = new 
Annotation[annotations.size()];
+                        annotations.toArray(anArray);
+                        validate(anArray, entry.getValue().getValue());
+                    }
+                }
+
+                //If a membership rule is defined on the property set, ensure 
the rule is satisfied. 
+                if (decl.getAnnotation(MembershipRule.class) != null)
+                {
+                    try 
+                    {
+                        String declClassName = decl.getQualifiedName();
+
+                        TypeDeclaration owningClass = decl.getDeclaringType();
+                        if (owningClass != null)
+                            declClassName = owningClass.getQualifiedName() + 
"$" + decl.getSimpleName();
+                            
+                           Class clazz = Class.forName(declClassName);
+                           Annotation a = d.getAnnotation(clazz);
+                           validateMembership(a);
+                    }
+                    catch (ClassNotFoundException cnfe)
+                    {
+                        //should not happen
+                    }
+                }
+            }
+        }
+
+        // If the declaration is a class or interface, validate its methods
+        // and fields.
+        if (d instanceof TypeDeclaration)
+        {
+            TypeDeclaration td = ((TypeDeclaration) d);
+            for (Declaration md : td.getMethods())
+                validate(md);
+            for (Declaration fd : td.getFields())
+                validate(fd);
+        }
+        // If the delcaration is a method, validate its parameters.
+        else if (d instanceof MethodDeclaration)
+        {
+            for (Declaration pd : ((MethodDeclaration) d).getParameters())
+                validate(pd);
+        }
+
+    }
+
+    /**
+     * This method ensures the membership constraints defined on a property set
+     * is satisfied.
+     * 
+     * @param propertySet the property set to validate
+     */
+    public static void validateMembership(Annotation propertySet)
+    {
+        Class c = propertySet.annotationType();
+        MembershipRule rule = (MembershipRule) c
+                .getAnnotation(MembershipRule.class);
+        if (rule == null)
+            return;
+        MembershipRuleValues ruleValue = rule.value();
+        String[] memberNames = rule.memberNames();
+        Method[] members = getMembers(c, memberNames);
+        int i = getNumOfMembersSet(propertySet, members);
+        if (ruleValue == MembershipRuleValues.ALL_IF_ANY)
+        {
+            if (i != 0 && i != members.length) 
+                throw new IllegalArgumentException("The membership rule on " + 
propertySet.toString() + 
+                    " is not satisfied. Either all members must be set or none 
is set");
+        }
+        else if (ruleValue == MembershipRuleValues.EXACTLY_ONE)
+        {
+            if (i != 1)
+                throw new IllegalArgumentException("The membership rule on " + 
propertySet.toString() + 
+                       " is not satisfied. Exactly one member must be set");
+        }
+        else if (ruleValue == MembershipRuleValues.AT_LEAST_ONE)
+        {
+            if (i < 1)
+                throw new IllegalArgumentException("The membership rule on " + 
propertySet.toString() + 
+                       " is not satisfied. At least one member must be set");
+        }
+        else if (ruleValue == MembershipRuleValues.AT_MOST_ONE)
+        {
+            if (i > 1)
+                throw new IllegalArgumentException("The membership rule on " + 
propertySet.toString() + 
+                       " is not satisfied. At most one member may be set");
+        }
+    }
+
+    private static Method[] getMembers(Class<? extends Annotation> c, String[] 
memberNames)
+    {
+        Method[] methods = null;
+        if (memberNames == null || memberNames.length == 0)
+        {
+            methods = c.getDeclaredMethods();
+        }
+        else
+        {
+            methods = new Method[memberNames.length];
+            for (int i = 0; i < memberNames.length; i++)
+            {
+                try
+                {
+                    methods[i] = c.getMethod(memberNames[i], (Class[]) null);
+                }
+                catch (Exception e)
+                {
+                    // method is not found, so the member is ignored.
+                }
+            }
+        }
+        return methods;
+    }
+
+    private static int getNumOfMembersSet(Annotation propertySet,
+            Method[] members)
+    {
+        int num = 0;
+        for (Method m : members)
+        {
+            Class returnType = m.getReturnType();
+            Object o = null;
+            try
+            {
+                o = m.invoke(propertySet, (Object[]) null);
+            }
+            catch (Exception e)
+            {
+                // This should never happen.
+                throw new RuntimeException(e);
+            }
+
+            if ((returnType == String.class && !((String) o)
+                    .equals(AnnotationMemberTypes.OPTIONAL_STRING))
+                    || (returnType == int.class && ((Integer) o).intValue() != 
AnnotationMemberTypes.OPTIONAL_INT)
+                    || (returnType == short.class && ((Short) o)
+                            .shortValue() != 
AnnotationMemberTypes.OPTIONAL_SHORT)
+                    || (returnType == long.class && ((Long) o).longValue() != 
AnnotationMemberTypes.OPTIONAL_LONG)
+                    || (returnType == float.class && ((Float) o)
+                            .floatValue() != 
AnnotationMemberTypes.OPTIONAL_FLOAT)
+                    || (returnType == double.class && ((Double) o)
+                            .doubleValue() != 
AnnotationMemberTypes.OPTIONAL_DOUBLE)
+                    || (returnType == char.class && ((Character) o)
+                            .charValue() != 
AnnotationMemberTypes.OPTIONAL_CHAR)
+                    || (returnType == byte.class && ((Byte) o).byteValue() != 
AnnotationMemberTypes.OPTIONAL_BYTE)
+                    || (returnType == boolean.class && !((Boolean) o)
+                            .booleanValue()))
+                       num++;
+        }
+        return num;
+    }
+
+    private static Collection<Annotation> getMemberTypeAnnotations(
+            Declaration decl)
+    {
+        Collection<Annotation> annotations = new ArrayList<Annotation>();
+        for (AnnotationMirror am : decl.getAnnotationMirrors())
+        {
+            AnnotationType at = am.getAnnotationType();
+            try
+            {
+                if (at.getContainingType() == null)
+                    continue;
+                String containingClassName = at.getContainingType()
+                        .getDeclaration().getQualifiedName();
+                if (containingClassName.equals(AnnotationMemberTypes.class
+                        .getName()))
+                {
+                    String memberTypeName = 
at.getDeclaration().getSimpleName();
+                    Class clazz = Class.forName(containingClassName + "$"
+                            + memberTypeName);
+                    Annotation a = decl.getAnnotation(clazz);
+                    if (null != a)
+                    {
+                        annotations.add(a);
+                    }
+                }
+            }
+            catch (ClassNotFoundException e)
+            {
+            }
+        }
+        return annotations;
+    }
+
+    private static synchronized void validate(Annotation[] annotations,
+            Object value) throws IllegalArgumentException
+    {
+
+        // Determine if the member is optional. This is done in a separate loop
+        // because a control property may have multiple constraints and the
+        // optional
+        // annotation may be declared after another constraint annotation.
+        boolean optional = false;
+        for (Annotation a : annotations)
+        {
+            if (a instanceof AnnotationMemberTypes.Optional)
+            {
+                optional = true;
+                break;
+            }
+        }
+
+        for (Annotation a : annotations)
+        {
+            if (a instanceof AnnotationMemberTypes.Text)
+                validateText((AnnotationMemberTypes.Text) a, value, optional);
+            else if (a instanceof AnnotationMemberTypes.Decimal)
+                validateDecimal((AnnotationMemberTypes.Decimal) a, value,
+                        optional);
+            else if (a instanceof AnnotationMemberTypes.Int)
+                validateInt((AnnotationMemberTypes.Int) a, value, optional);
+            else if (a instanceof AnnotationMemberTypes.Date)
+                validateDate((AnnotationMemberTypes.Date) a, value, optional);
+            else if (a instanceof AnnotationMemberTypes.FilePath)
+                validateFilePath((AnnotationMemberTypes.FilePath) a, value,
+                        optional);
+            else if (a instanceof AnnotationMemberTypes.JndiName)
+                validateJndiName((AnnotationMemberTypes.JndiName) a, value,
+                        optional);
+            else if (a instanceof AnnotationMemberTypes.QName)
+                validateQName((AnnotationMemberTypes.QName) a, value, 
optional);
+            else if (a instanceof AnnotationMemberTypes.URI)
+                validateURI((AnnotationMemberTypes.URI) a, value, optional);
+            else if (a instanceof AnnotationMemberTypes.URL)
+                validateURL((AnnotationMemberTypes.URL) a, value, optional);
+            else if (a instanceof AnnotationMemberTypes.URN)
+                validateURN((AnnotationMemberTypes.URN) a, value, optional);
+            else if (a instanceof AnnotationMemberTypes.XML)
+                validateXML((AnnotationMemberTypes.XML) a, value, optional);
+        }
+    }
+
+    private static void validateXML(AnnotationMemberTypes.XML a, Object value,
+            boolean optional)
+    {
+    }
+
+    private static void validateURN(AnnotationMemberTypes.URN a, Object value,
+            boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to an URN property must be of type 
java.lang.String.");
+        }
+
+        URI.create((String) value);
+    }
+
+    private static void validateURL(AnnotationMemberTypes.URL a, Object value,
+            boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to an URL property must be of type 
java.lang.String.");
+        }
+
+        try
+        {
+            new URL((String) value);
+        }
+        catch (MalformedURLException mue)
+        {
+            error("The value, " + value
+                    + ", assigned to the URL property is a malformed URL.", 
mue);
+        }
+    }
+
+    private static void validateURI(AnnotationMemberTypes.URI a, Object value,
+            boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to an URI property must be of type 
java.lang.String.");
+        }
+
+        URI.create((String) value);
+    }
+
+    private static void validateQName(AnnotationMemberTypes.QName a,
+            Object value, boolean optional)
+    {
+    }
+
+    private static void validateJndiName(AnnotationMemberTypes.JndiName a,
+            Object value, boolean optional)
+    {
+    }
+
+    private static void validateFilePath(AnnotationMemberTypes.FilePath a,
+            Object value, boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to a FilePath property must be of type 
java.lang.String.");
+        }
+
+        File file = new File((String) value);
+        if (!file.isFile() || !file.canRead())
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to a FilePath property must be a readable 
file.");
+        }
+
+    }
+
+    private static void validateDate(AnnotationMemberTypes.Date a,
+            Object value, boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to a date property must be of type 
java.lang.String.");
+        }
+
+        try
+        {
+            Date date = parseDate((String) value);
+
+            String minValue = a.minValue();
+            if (minValue != null && minValue.length() > 0)
+            {
+                Date minDate = parseDate(a.minValue());
+                if (minDate.compareTo(date) > 0)
+                {
+                    SimpleDateFormat format = new 
SimpleDateFormat("yyyy/MM/dd");
+                    error("The date, "
+                            + format.format(date)
+                            + ", assigned to a date property is earlier than 
the earliest date allowed: "
+                            + format.format(minDate));
+                }
+            }
+            String maxValue = a.maxValue();
+            if (maxValue != null && maxValue.length() > 0)
+            {
+                Date maxDate = parseDate(a.maxValue());
+                if (maxDate.compareTo(date) < 0)
+                {
+                    SimpleDateFormat format = new 
SimpleDateFormat("yyyy/MM/dd");
+                    error("The date, "
+                            + format.format(date)
+                            + ", assigned to a date property is later than the 
latest date allowed: "
+                            + format.format(maxDate));
+                }
+            }
+        }
+        catch (ParseException pe)
+        {
+            error("Value assigned to a date property must be in the form 
'YYYY/MM/DD'");
+        }
+
+    }
+
+    private static Date parseDate(String s) throws ParseException
+    {
+        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
+        format.setLenient(false);
+        return format.parse(s);
+    }
+
+    /**
+     * @param value
+     * @return
+     */
+    private static void validateInt(AnnotationMemberTypes.Int a, Object value,
+            boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_INT)))
+            return;
+
+        int intValue = 0;
+
+        if (value instanceof String)
+        {
+            try
+            {
+                intValue = Integer.parseInt((String) value);
+            }
+            catch (NumberFormatException nfe)
+            {
+                error("The value ,"
+                        + value
+                        + ", assigned to an int property does not represent an 
integer.");
+            }
+        }
+        else if (value instanceof Integer)
+        {
+            intValue = ((Integer) value).intValue();
+        }
+        else
+        {
+            error("The value, "
+                    + value
+                    + ", assigned to an int property must be of type 
java.lang.String or int.");
+        }
+
+        if (intValue < a.minValue())
+            error("The value, "
+                    + intValue
+                    + ", assigned to an int property is less than the minimum 
value allowed: "
+                    + a.minValue() + ".");
+        else if (intValue > a.maxValue())
+            error("The value, "
+                    + intValue
+                    + ", assigned to an int property exeeds the maximum value 
allowed: "
+                    + a.maxValue() + ".");
+    }
+
+    private static void validateDecimal(AnnotationMemberTypes.Decimal a,
+            Object value, boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+        {
+            error("Value assigned to a decimal property must be of type 
java.lang.String.");
+        }
+
+        String str = (String) value;
+
+        try
+        {
+            long num = Long.parseLong(str);
+
+            if (num < Long.parseLong(a.minValue()))
+                error("The value, "
+                        + num
+                        + ", assigned to a decimal property is less than the 
the minimum value allowed: "
+                        + a.minValue() + ".");
+
+            if (num > Long.parseLong(a.maxValue()))
+                error("The value, "
+                        + num
+                        + ", assigned to a decimal property exceeds the 
maximum value allowed: "
+                        + a.maxValue() + ".");
+        }
+        catch (NumberFormatException nfe)
+        {
+            error("The value, "
+                    + str
+                    + ", assigned to a decimal property does not represent a 
number.");
+        }
+
+        int decimalPos = str.indexOf('.');
+
+        if (decimalPos == -1)
+            return;
+
+        if (str.length() - decimalPos - 1 > a.places())
+            error("The decimal places in the value, " + str
+                    + ", assigned to a decimal property exceeds " + a.places()
+                    + ", the number of decimal places allowed.");
+
+    }
+
+    private static void validateText(AnnotationMemberTypes.Text a,
+            Object value, boolean optional)
+    {
+        if (optional
+                && (value == null || value
+                        .equals(AnnotationMemberTypes.OPTIONAL_STRING)))
+            return;
+
+        if (!(value instanceof String))
+            error("The value, "
+                    + value
+                    + ", assigned to a text property must be of type 
java.lang.String.");
+
+        String str = (String) value;
+        if (str.length() > a.maxLength())
+            error("The value, "
+                    + str
+                    + ", assigned to a text property exceeds the maximum 
length allowed: "
+                    + a.maxLength());
+
+        if (a.isLong())
+        {
+            try
+            {
+                Long.parseLong(str);
+            }
+            catch (NumberFormatException nfe)
+            {
+                error("The value, "
+                        + str
+                        + ", assigned to a text property with a long number 
constraint does not represent a long number.");
+            }
+        }
+
+    }
+
+    private static void error(String message)
+    {
+        error(message, null);
+    }
+
+    private static void error(String message, Throwable t)
+    {
+        throw new IllegalArgumentException(message, t);
+    }
+
+}

Modified: 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java&r1=126630&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java
      (original)
+++ 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/bean/ControlBean.java
      Thu Jan 27 09:39:56 2005
@@ -615,6 +615,7 @@
      */
     protected void setControlProperty(PropertyKey key, Object o)
     {
+        AnnotationConstraintValidator.validate(key, o);
         _properties.setProperty(key, o);
     }
 

Modified: 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java&r1=126630&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java
      (original)
+++ 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlAnnotationProcessor.java
      Thu Jan 27 09:39:56 2005
@@ -38,6 +38,7 @@
 import org.apache.beehive.controls.api.bean.ControlInterface;
 import org.apache.beehive.controls.api.properties.PropertySet;
 
+import org.apache.beehive.controls.runtime.bean.AnnotationConstraintValidator;
 import org.apache.beehive.controls.runtime.generator.*;
 
 public class ControlAnnotationProcessor extends TwoPhaseAnnotationProcessor
@@ -60,6 +61,20 @@
         else if (decl.getAnnotation(ControlExtension.class) != null)
         {
             genClass = new AptControlInterface(decl, env);
+
+            // When a control extension is declared, values may be assigned to 
+            // the properties of the parent controls.  The property constraint
+            // validator is called here to ensure all values assigned satisfy 
any 
+            // constraints declared in the properties.
+            try
+            {
+                AnnotationConstraintValidator.validate(decl);
+            }
+            catch (IllegalArgumentException iae)
+            {
+                printError(decl, "propertyset.illegal.argument.error", 
iae.getMessage());
+            }
+            
         }
         else if (decl.getAnnotation(ControlImplementation.class) != null)
         {

Modified: 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java&r1=126630&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java
        (original)
+++ 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/ControlClientAnnotationProcessor.java
        Thu Jan 27 09:39:56 2005
@@ -25,6 +25,7 @@
 import com.sun.mirror.declaration.*;
 import com.sun.mirror.apt.*;
 import com.sun.mirror.type.*;
+import com.sun.tools.corba.se.idl.InvalidArgument;
 
 import java.util.*;
 
@@ -33,6 +34,7 @@
 import org.apache.beehive.controls.runtime.generator.Generator;
 import org.apache.beehive.controls.runtime.generator.GeneratorOutput;
 import org.apache.beehive.controls.runtime.generator.CodeGenerator;
+import org.apache.beehive.controls.runtime.bean.AnnotationConstraintValidator;
 import org.apache.beehive.controls.runtime.bean.ControlBeanContext;
 import org.apache.beehive.controls.api.bean.*;
 
@@ -60,6 +62,19 @@
 
         if ( d instanceof TypeDeclaration )
             checkControlClientType( (TypeDeclaration)d );
+        
+        // When a control is instantiated declaratively, values may be 
assigned to 
+        // the control's properties declaratively as well.  The property 
constraint
+        // validator is called here to ensure all values assigned satisfy any 
+        // constraints declared in the properties.
+        try
+        {
+            AnnotationConstraintValidator.validate(d);
+        }
+        catch (IllegalArgumentException iae)
+        {
+            printError(d, "propertyset.illegal.argument.error", 
iae.getMessage());
+        }
     }
 
     /**

Modified: 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties&r1=126630&p2=incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties
   (original)
+++ 
incubator/beehive/trunk/controls/src/runtime/org/apache/beehive/controls/runtime/generator/apt/strings.properties
   Thu Jan 27 09:39:56 2005
@@ -33,3 +33,6 @@
 controls.client.manifest.ioerror= \
 An error occurred writing the controls client manifest.
 
+propertyset.illegal.argument.error=\
+A value assigned to a control property does not satisfy \
+its constraints. Cause: {0} 
\ No newline at end of file

Added: 
incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControl.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControl.java?view=auto&rev=126631
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControl.java
        Thu Jan 27 09:39:56 2005
@@ -0,0 +1,31 @@
+package org.apache.beehive.controls.test.controls.property;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.apache.beehive.controls.api.bean.AnnotationMemberTypes;
+import org.apache.beehive.controls.api.bean.ControlInterface;
+import org.apache.beehive.controls.api.properties.PropertySet;
+
[EMAIL PROTECTED]
+public interface PropertyConstraintControl
+{
+
+    @PropertySet
+    @Target ({ElementType.FIELD, ElementType.TYPE})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Person
+    {
+        @AnnotationMemberTypes.Text(maxLength=8)
+        public String name() default "";
+        @AnnotationMemberTypes.Date(minValue="1900/1/1")
+        public String dob() default "";
+        @AnnotationMemberTypes.Int (minValue=0, maxValue=130)
+        public int age() default AnnotationMemberTypes.OPTIONAL_INT;
+    }
+
+    public String hello();
+
+}

Added: 
incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControlImpl.jcs
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControlImpl.jcs?view=auto&rev=126631
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/controls/test/src/controls/org/apache/beehive/controls/test/controls/property/PropertyConstraintControlImpl.jcs
     Thu Jan 27 09:39:56 2005
@@ -0,0 +1,19 @@
+package org.apache.beehive.controls.test.controls.property;
+
+import org.apache.beehive.controls.api.bean.ControlImplementation;
+import org.apache.beehive.controls.api.bean.Extensible;
+import org.apache.beehive.controls.api.context.ControlBeanContext;
+import org.apache.beehive.controls.api.context.Context;
+
[EMAIL PROTECTED]
+public class PropertyConstraintControlImpl implements PropertyConstraintControl
+{
+    @Context ControlBeanContext ctx;
+
+    public String hello()
+    {
+        Person person = (Person)ctx.getControlPropertySet(Person.class);
+        return "Hello " + person.name();
+    }
+
+}

Added: 
incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropertyConstraint.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropertyConstraint.java?view=auto&rev=126631
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/controls/test/src/drivers/org/apache/beehive/controls/test/driver/property/DrivePropertyConstraint.java
     Thu Jan 27 09:39:56 2005
@@ -0,0 +1,108 @@
+package org.apache.beehive.controls.test.driver.property;
+
+import org.apache.beehive.test.tools.milton.common.Report;
+import 
org.apache.beehive.controls.test.controls.property.PropertyConstraintControl;
+import 
org.apache.beehive.controls.test.controls.property.PropertyConstraintControlBean;
+
+/* Tests property constraint validator
+ */
+public class DrivePropertyConstraint
+{
+       private PropertyConstraintControlBean myControl;
+
+       public void setControl(PropertyConstraintControlBean aControl){
+
+               myControl=aControl;
+       }
+
+       public Report doTest(){
+
+               Report report=new Report();
+        report.setStatus(Report.PASS);
+
+               if (myControl==null){
+                       report.setStatus(Report.FAIL);
+                       report.setMessage("The control is NULL");
+            return report;
+               }
+
+        //Assigning a valid value to a text property
+        try
+        {
+            myControl.setName("Bob");
+        }
+        catch (IllegalArgumentException e)
+        {
+            report.setStatus(Report.FAIL);
+            report.setMessage("Assigning a valid value to a text property 
generated an exception.");
+            return report;
+        }
+
+        //Assigning a value longer than a text property's max length
+        try
+        {
+            myControl.setName("Some name longer than 10 characters");
+            report.setStatus(Report.FAIL);
+            report.setMessage("Assigning a value longer than a text property's 
max length does not generate an exception.");
+            return report;
+        }
+        catch (IllegalArgumentException e)
+        {
+            //expected exception
+        }
+
+        //Assigning a valid date to a date property
+        try
+        {
+            myControl.setDob("1990/2/12");
+        }
+        catch (IllegalArgumentException e)
+        {
+            report.setStatus(Report.FAIL);
+            report.setMessage("Assigning a valid date to a date property 
generated an exception.");
+            return report;
+        }
+
+        //Assigning an invalid date to a date property
+        try
+        {
+            myControl.setName("Jan 12, 1993");
+            report.setStatus(Report.FAIL);
+            report.setMessage("Assigning a value with the an incorrect date 
format to a date property does not generate an exception.");
+            return report;
+        }
+        catch (IllegalArgumentException e)
+        {
+            //expected exception
+        }
+
+        //Assigning a valid date to a date property
+        try
+        {
+            myControl.setDob("1990/2/12");
+        }
+        catch (IllegalArgumentException e)
+        {
+            report.setStatus(Report.FAIL);
+            report.setMessage("Assigning a valid date to a date property 
generated an exception.");
+            return report;
+        }
+
+        //Assigning an invalid number to an int property
+        try
+        {
+            myControl.setAge(-1);
+            report.setStatus(Report.FAIL);
+            report.setMessage("Assigning a value less than an int property's 
min. value does not generate an exception.");
+            return report;
+        }
+        catch (IllegalArgumentException e)
+        {
+            //expected exception
+        }
+
+
+               return report;
+       }
+
+}

Modified: 
incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java?view=diff&rev=126631&p1=incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java&r1=126630&p2=incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java&r2=126631
==============================================================================
--- 
incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java
     (original)
+++ 
incubator/beehive/trunk/controls/test/src/units/org/apache/beehive/controls/test/jpf/property/TestProperty.java
     Thu Jan 27 09:39:56 2005
@@ -76,6 +76,16 @@
     }
 
     /**
+     * Tests control constraint validator
+     */
+    @Freq("checkin")
+    public void testPropertyConstraintValidator() throws Exception
+    {
+        
assertReport("/controlsWeb/property/property_constraints/Controller.jpf");
+    }
+
+
+    /**
      * Tests vetoing the change of a constrained property
      */
        @Freq("checkin")

Added: 
incubator/beehive/trunk/controls/test/webapps/controlsWeb/property/property_constraints/Controller.jpf
Url: 
http://svn.apache.org/viewcvs/incubator/beehive/trunk/controls/test/webapps/controlsWeb/property/property_constraints/Controller.jpf?view=auto&rev=126631
==============================================================================
--- (empty file)
+++ 
incubator/beehive/trunk/controls/test/webapps/controlsWeb/property/property_constraints/Controller.jpf
      Thu Jan 27 09:39:56 2005
@@ -0,0 +1,66 @@
+/*
+ *
+ * N E T U I
+ *
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * All Rights Reserved. Unpublished rights reserved under the copyright laws
+ * of the United States. The software contained on this media is proprietary
+ * to and embodies the confidential technology of BEA Systems, Inc. The
+ * possession or receipt of this information does not convey any right to
+ * disclose its contents, reproduce it, or use,  or license the use,
+ * for manufacture or sale, the information or anything described
+ * therein. Any use, disclosure, or reproduction without BEA System's
+ * prior written permission is strictly prohibited.
+ *
+ * $Header:$
+ */
+package property.property_constraints;
+
+import org.apache.beehive.netui.pageflow.PageFlowController;
+import org.apache.beehive.netui.pageflow.Forward;
+import org.apache.beehive.netui.pageflow.FormData;
+import org.apache.beehive.netui.pageflow.annotations.Jpf;
+
+import org.apache.beehive.controls.api.bean.Control;
+import org.apache.beehive.controls.api.bean.ControlBean;
+import 
org.apache.beehive.controls.test.controls.property.PropertyConstraintControlBean;
+import 
org.apache.beehive.controls.test.driver.property.DrivePropertyConstraint;
+import org.apache.beehive.test.tools.milton.common.Report;
+
+/* Tests control property constraint validator
+ */
+
[EMAIL PROTECTED](
+    forwards = {
+        @Jpf.Forward(name=Report.RESULTS,path = Report.RESULTSJSP)
+    })
+public class Controller extends PageFlowController
+{
+    @Control
+    public PropertyConstraintControlBean myControl;
+
+    /**
+     * @jpf:action
+     */
+    @Jpf.Action(
+        )
+    protected Forward begin(){
+
+       Report report=new Report();
+       DrivePropertyConstraint driver=new DrivePropertyConstraint();
+       try{
+               driver.setControl(myControl);
+               report=driver.doTest();
+        }
+        catch(Exception e){
+
+               report.setStatus(Report.FAIL);
+               report.setExceptionStack(e);
+        }
+        return new Forward(Report.RESULTS, Report.KEY, report);
+    }
+
+}
+
+

Reply via email to