Package org.apache.beehive.controls.runtime.bean

Source Code of org.apache.beehive.controls.runtime.bean.AnnotationConstraintValidator

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;

/**
* 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 {@link org.apache.beehive.controls.api.bean.AnnotationMemberTypes AnnotationMemberTypes} and
* {@link 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 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;
    }

    protected 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.");
        }

//Temporarily commenting out the following check on FilePath until
//an agreement is reached on what is a valid FilePath.
//       
//        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
        {
          String format = a.format();
         
            Date date = parseDate(format , (String) value);

            String minValue = a.minValue();
            if (minValue != null && minValue.length() > 0)
            {
                Date minDate = parseDate(format, a.minValue());
                if (minDate.compareTo(date) > 0)
                {
                    error("The date, "
                            + value
                            + ", assigned to a date property is earlier than the earliest date allowed: "
                            + minValue);
                }
            }
            String maxValue = a.maxValue();
            if (maxValue != null && maxValue.length() > 0)
            {
                Date maxDate = parseDate(format, a.maxValue());
                if (maxDate.compareTo(date) < 0)
                {
                    error("The date, "
                            + value
                            + ", assigned to a date property is later than the latest date allowed: "
                            + maxValue);
                }
            }
        }
        catch (ParseException pe)
        {
            error("Value assigned to a date property is not in the specified format.");
        }

    }

    public static Date parseDate(String format, String value) throws ParseException
    {
        SimpleDateFormat sdFormat = new SimpleDateFormat(format);
        sdFormat.setLenient(false);
        return sdFormat.parse(value);
    }

    /**
     * @param value
     * @return
     */
    private static void validateInt(AnnotationMemberTypes.Int a, Object value,
            boolean optional)
    {
        if (optional
                && (value == null ||
                        value.equals(AnnotationMemberTypes.OPTIONAL_STRING) ||
                        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) ||
                    value.equals(AnnotationMemberTypes.OPTIONAL_FLOAT) ||
                    value.equals(AnnotationMemberTypes.OPTIONAL_DOUBLE)))
            return;

        double doubleValue = 0;
        String doubleString = null;
       
        if (value instanceof String)
        {
            doubleValue = Double.parseDouble((String)value);
            doubleString = (String)value;
        }
        else if (value instanceof Float)
        {
            doubleValue = ((Float)value).doubleValue();
            doubleString = ((Float)value).toString();
        }
        else if (value instanceof Double)
        {
            doubleValue = ((Double)value).doubleValue();
            doubleString = ((Double)value).toString();
        }
        else
        {
            error("The value, "
                    + value
                    + ", assigned to a decimal property must be of type float, double, or java.lang.String.");
        }

        if (doubleValue < a.minValue())
            error("The value, "
                    + doubleValue
                    + ", assigned to a decimal property is less than the the minimum value allowed: "
                    + a.minValue() + ".");

        if (doubleValue > a.maxValue())
            error("The value, "
                    + doubleValue
                    + ", assigned to a decimal property exceeds the maximum value allowed: "
                    + a.maxValue() + ".");

        int decimalPos = doubleString.indexOf('.');

        if (decimalPos == -1)
            return;

        if (doubleString.length() - decimalPos - 1 > a.places())
            error("The decimal places in the value, " + doubleString
                    + ", 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);
    }

}
TOP

Related Classes of org.apache.beehive.controls.runtime.bean.AnnotationConstraintValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.