/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * ValidationManager.java        April 14, 2003, 5:29 PM
 */

package com.sun.enterprise.tools.common.validation;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import org.netbeans.modules.schema2beans.BaseBean;

import com.sun.enterprise.tools.common.validation.Constants;
import com.sun.enterprise.tools.common.validation.constraints.CardinalConstraint;
import com.sun.enterprise.tools.common.validation.constraints.Constraint;
import com.sun.enterprise.tools.common.validation.constraints.data.Argument;
import com.sun.enterprise.tools.common.validation.constraints.data.Arguments;
import com.sun.enterprise.tools.common.validation.constraints.data.CheckInfo;
import com.sun.enterprise.tools.common.validation.constraints.data.Constraints;
import com.sun.enterprise.tools.common.validation.data.Check;
import com.sun.enterprise.tools.common.validation.data.Element;
import com.sun.enterprise.tools.common.validation.data.Validation;
import com.sun.enterprise.tools.common.validation.data.Parameter;
import com.sun.enterprise.tools.common.validation.data.Parameters;
import com.sun.enterprise.tools.common.validation.util.BundleReader;
import com.sun.enterprise.tools.common.validation.util.ObjectFactory;
import com.sun.enterprise.tools.common.validation.util.Utils;


/**
 * {@link ValidationManager} is an object that provides Validation functionality,
 * through its <code>validate( )</code> function.
 * <code>{@link ValidationManagerFactory}</code> creates 
 * <code>{@link ValidationManager}</code> based on the Validation File.
 * Validation File specifies Validation rules i.e.
 * which Constraints to apply to which elements. Objects are validated by 
 * applying specified Constraints to its elements. Validation file is xml file
 * based on  <code>validation.dtd</code>. If no Validation File is specified,
 * <code>ValidationManagerFactory</code> returns default 
 * <code>ValidationManager</code>. Default <code>ValidationManager</code> is 
 * based on default Validation File. Default Validation File defines Constraints
 * for 8.0 Sun DTDs.
 * <p>
 * Validations are performed, recursively on the given Object.
 * Two types of Validations are perfomed, Structural validations and Specified
 * validations. Structural validations are expressed through 
 * <code>{@link CardinalConstraint}</code>. <code>CardinalConstraint</code> is 
 * an implicit Constraint. Its always applied to each of the element; you dont
 * need to specify it explicitly. Whereas , other Constaints need to be
 * explicitly specified for each element you wanted to apply it to.
 * Constraints to be applied are specified through Validation File.
 * You can also define, your own custom <code>Constraint</code>s and apply them 
 * to any elements. When you define your own <code>Constraint</code>s, you 
 * need to provide information about them to the framework. This information is
 * provided through Constraints File. Constraints File is xml file, based on
 * <code>constraints.dtd</code>. Constraints File to use is specified through
 * system property <code>constraints.file</code>. You can override 
 * <code>Constraint</code>s provided by framework , by providing your own.
 *
 * @see Constraint
 * @see CardinalConstraint
 * @see Validatee
 * @see ValidationManagerFactory
 *
 * @author  Rajeshwar Patil
 * @version %I%, %G%
 */
public class ValidationManager {
    /*
    ValidationManager is an object that constructs Validator objects for all
    the Validatees. ValidationManager maintains a map of xpaths to Validators.
    It constructs this by reading Validation File. Validation File specifies
    Constraints to be applied to the elements.
    Every Validatee has a corresponding Validator object and this object
    knows how to validate its Validatee. Validator maintains a list of
    Constraints that needs to be applied for each of the element of its
    Validatee. Constraint objects are built using files, Constraints File 
    and Validation File.
    */

    /**
     * A prefix used to construct the getter method name of the bean.
     */
    private static String GET_PREFIX = "get";                           //NOI18N


    /**
     * A prefix used to construct the setter method name of the bean.
     */
    private static String SET_PREFIX = "set";                           //NOI18N


    /**
     * A prefix used to construct the size method name of the bean.
     */
    private static String SIZE_PREFIX = "size";                         //NOI18N
    

    /**
     * A file that is used specify infromation for the constraints provided
     * by this framework. <code>Constraint</code> objects are built using the
     * information in this file.
     */
    private static final String defaultConstraintsFile = 
        "com/sun/enterprise/tools/common/validation/constraints/" +     //NOI18N
            "constraints.xml";                                          //NOI18N

    
    /**
     * A Cardinal Constraint class name.
     */
    private static final String CARDINAL_CONSTRAINT_CLASS =
        "com.sun.enterprise.tools.common.validation.constraints." +     //NOI18N
            "CardinalConstraint";                                       //NOI18N


    /**
     * A file that is used to specify <code>Constraints<code> to be 
     * applied to various elements. xpaths are used to specify elements.
     * While constructing this object, clients of this framework can specify
     * their own file by providing the file name to the appropriate
     * constructor. If client do not, then the default file( specified by 
     * <code>defaultValidationFile</code> ) is  used.
     */
    private String validationFile = null;

    
    /**
     * A default Validation file that specify <code>Constraints<code> to be
     * applied to various elements. xpaths are used to specify elements.
     * This file is used by framework if client do not specify one.
     * This file contains default validation rules.
     */
    private String defaultValidationFile = 
           "com/sun/enterprise/tools/common/validation/validation.xml"; //NOI18N
    
    
    /**
     * A map that stores for each <code>Validatee</code> object, the xpath and
     * the corresponding <code>Validator</code> object.
     */
    private HashMap xpathToValidator = null;


    /**
     * A root bean representing validation.xml
     */
    private Validation validation = null;


    /**
     * A root bean representing constaints.xml
     */
    private Constraints constraints = null;

    
    /**
     * An object providing utilities
     */
    private Utils utils = null;

    
    /** Creates a new instance of <code>ValidationManager</code> */
    public ValidationManager() {
        utils = new Utils();
    }


    /** Creates a new instance of <code>ValidationManager</code> */
    public ValidationManager(String validationFile) {
        if(null != validationFile){
            this.validationFile = validationFile;
        }
        
        utils = new Utils();
    }

    
    /**
     * Validates the given <code>Object</code>.
     * Validatee Implementation for the given <code>object</code> must be
     * provided. Validatee Implementation of an object is a 
     * <code>Validatee</code> wrapper around it. Validatee Implementation
     * of an object is specified to framework through an Implementation File.
     * Implementation File is a <code>.properties</code> file, with name-value
     * pair entries in it. An entry in Implementation File specifies the object
     * name and the corresponding Validatee Implementation.
     * Implementation File to use, is specified to framework through system
     * property <code>impl.file</code>
     *
     * @param object the object to be validated
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    public Collection validate(Object object){
        //This method get called in case of Objects that are not Validatee.
        //This method, essentially, gets the Object's Validatee Implementation 
        //and uses it instead.

        Collection collection = new ArrayList();

        if(object != null){
            boolean validateeImplFound = false;
            String validateeImpl = null;

            String implFile = 
                System.getProperty("impl.file");                            //NOI18N
            ///System.out.println(implFile);

            //User specified impl file overrides the default imple file
            //default impl file -- Constants.IMPL_FILE
            if(implFile != null){
                validateeImpl = getValidateeImplementation(object, implFile);
            }

            //Using default impl file; user specified not available
            if(validateeImpl == null){
                validateeImpl = 
                    getValidateeImplementation(object, Constants.IMPL_FILE);
            }

            //switch the BundleReader back to read from bundle file
            BundleReader.setBundle(Constants.BUNDLE_FILE);

            if(validateeImpl != null){
                Validatee validatee =
                    (Validatee)ObjectFactory.newInstance(validateeImpl, object);
                if(validatee != null){
                    collection = validate(validatee);
                }
            } else {
                Class classObject = utils.getClass(object);
                String className = classObject.getName();

                String format = BundleReader.getValue(
                    "MSG_given_object_is_not_validatee");               //NOI18N
                Object[] arguments = new Object[]{className};
                System.out.println(MessageFormat.format(format, arguments));
            }            

        }
        return collection;
   }   


    /**
     * Validates the given <code>Validatee</code>.
     * 
     * @param validatee the object to be validated
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    public Collection validate(Validatee validatee){
        //This method applies Cardinal constraint and Custom constraints defined
        //for each of the elements of the validatee. It recurses for elements 
        //that are objects.
        ArrayList failures = new ArrayList();
        if(validatee != null){
            String xpath = validatee.getXPath();

            ArrayList elementNames = validatee.getElementNames();
            ArrayList elementDtdNames = validatee.getElementDtdNames();
            int noOfElements = elementNames.size();
            String elementName = null;
            String elementDtdName = null;
            int count = 0;

            for(int i=0; i<noOfElements; i++){
                elementName = (String)elementNames.get(i);
                elementDtdName = (String)elementDtdNames.get(i);

                //apply Cardinal Constraint
                failures.addAll(validateCardinalConstraint(validatee, elementName, 
                        elementDtdName));

                //apply Other Constraints
                boolean isBean = validatee.isBeanElement(elementName);
                if(isBean){
                    //Recurse if an Object
                    failures.addAll(recurse(elementName, validatee));
                } else {
                    Validator validator = getValidator(xpath);
                    if(null != validator){
                        failures.addAll(validator.validate(elementName,
                                elementDtdName, validatee));
                    } else {
                        ///String format = BundleReader.getValue(
                        ///    "MSG_No_definition_for");                //NOI18N
                        ///Object[] arguments = 
                        ///    new Object[]{"Validator", xpath};        //NOI18N
                        ///System.out.println(
                        ///    MessageFormat.format(format, arguments));
                    }
                }
           }
       }
       return failures;
    }


    /**
     * Validates the given Element.
     * 
     * @param property the Element to be validated
     * @param absoluteDtdName the complete dtd name of <code>property</code>
     * @param fieldName the name of the GUI field, associated 
     * with <code>property</code>
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    public Collection validateIndividualProperty(String property,
            String absoluteDtdName, String fieldName){
        ArrayList failures = new ArrayList();

        String xpath = utils.getParentName(absoluteDtdName,
                Constants.XPATH_DELIMITER_CHAR);
        Validator validator = getValidator(xpath);
        if(null != validator){
            failures.addAll(validator.validateIndividualProperty(
                property, absoluteDtdName, fieldName));
        } else {
            ///String format = BundleReader.getValue(
            ///    "MSG_No_definition_for");                            //NOI18N
            ///Object[] arguments = 
            ///    new Object[]{"Validator", xpath};                    //NOI18N
            ///System.out.println(
            ///    MessageFormat.format(format, arguments));
        }

        return failures;
    }


    /**
     * Recurses by calling the appropriate methods.
     * This method is called when the given element to be validated is
     * an object itself(<code>Validatee</code>).
     * 
     * @param elementName the given element to be validated
     * @param validatee the given <code>Validatee</code> object; the
     * element of which needs to be validated
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    private Collection recurse(String elementName, Validatee validatee){
        ArrayList failures = new ArrayList();
        boolean isIndexed = validatee.isIndexed(elementName);

        if(isIndexed){
            failures.addAll(validateBeans(elementName, validatee));
        } else {
            failures.addAll(validateBean(elementName, validatee));
        }
        return failures;
    }


    /**
     * Validates the given element of given <code>Validatee</code>
     * for Cardinality.This method is called for each and every 
     * element of the Validatee. You does not need to define Cardinal
     * cosntraints in Validation File. Cardinal constraint is implicity
     * applied to each and every element.
     * 
     * @param elementName the given element to be validated
     * @param elementDtdName the dtd name of the given element
     * @param validatee the given <code>Validatee</code> object; the
     * element which needs to be validated
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    private Collection validateCardinalConstraint(Validatee validatee,
            String elementName, String elementDtdName){
        ArrayList failures = new ArrayList();
        int cardinal = validatee.getElementCardinal(elementName);
        CardinalConstraint constraint = getCardinalConstraint(cardinal);

        switch(cardinal){
            case Constants.MANDATORY_ARRAY :
            case Constants.OPTIONAL_ARRAY: {
                Object[] elements = 
                    (Object [])validatee.getElements(elementName);
                String name = validatee.getIndexedXPath() +
                    Constants.XPATH_DELIMITER + elementDtdName;
                ///String name = validatee.getXPath() +
                    ///Constants.XPATH_DELIMITER + elementDtdName;
                failures.addAll(constraint.match(elements, name));
                break;
            }
            case Constants.OPTIONAL_ELEMENT : {
                Object element = validatee.getElement(elementName);
                break;
            }
            case Constants.MANDATORY_ELEMENT :
            default : {
                Object element = validatee.getElement(elementName);
                String name = validatee.getIndexedXPath() +
                    Constants.XPATH_DELIMITER + elementDtdName;
                ///String name = validatee.getXPath() +
                    ///Constants.XPATH_DELIMITER + elementDtdName;
                failures.addAll(constraint.match(element, name));
            }
            break;
        }
        return failures;
    }


    /**
     * Returns the <code>Validator</code> for the given xpath.
     * Returns <code>null</code> if there is no <code>Validator</code>
     * object for the given xpath.
     * <code>ValidationManager</code> constructs the <code>Validator</code>
     * objects for xpaths using Validation File and Constraints File.
     * 
     * @param xpath the given xpath.
     *
     * @return <code>Validator</code> the Corresponding validator object,if any.
     */
    private Validator getValidator(String xpath){
        Validator validator = null;
        if(null == xpathToValidator){
            constructXpathToValidator();
        }
        if(null != xpathToValidator) {
            validator = (Validator)xpathToValidator.get(xpath);
        }
        return validator;
    }


    /**
     * Constructs the xpath to <code>Validator</code> map.
     * Uses Validation File and Constraints File to build the map.
     */
    private void constructXpathToValidator(){
        //read validation file and construct xPathToValidator Map

        if(null == validation){
            constructValidation();
        }

        if(null == validation){
            String format = 
                BundleReader.getValue("Warning_Not_available");         //NOI18N
            Object[] arguments = new Object[]{"Validation Data"};       //NOI18N
            System.out.println(MessageFormat.format(format, arguments));
            return;
        }


        xpathToValidator = new HashMap();
        int noOfElements = validation.sizeElement();
        Element element = null;
        Validator validator = null;
	String elementName = null;
	String beanName = null;	 
        Check check = null;
        String checkName = null;

        for(int i=0; i<noOfElements; i++) {
            element = validation.getElement(i);
            elementName = utils.getName(element.getName(),
                Constants.XPATH_DELIMITER_CHAR);
            beanName = utils.getParentName(element.getName(),
                Constants.XPATH_DELIMITER_CHAR);
            validator = (Validator) xpathToValidator.get(beanName);
            ///String format = 
            ///    BundleReader.getValue("Name_Value_Pair_Format");     //NOI18N
            ///Object[] arguments;
            if(null == validator) {
                validator = new Validator();
                xpathToValidator.put(beanName,  validator);
                ///format =
                ///    BundleReader.getValue("Name_Value_Pair_Format"); //NOI18N
                ///arguments = 
                ///    new Object[]{"Bean Name", beanName};             //NOI18N
                ///System.out.println(MessageFormat.format(format, arguments));
                ///arguments = new Object[]{"Validator", validator};    //NOI18N
                ///System.out.println(MessageFormat.format(format, arguments));
            }

            int noOfChecks = element.sizeCheck();
            Constraint constraint = null;
            for(int j=0; j<noOfChecks; j++){
                check = element.getCheck(j);
                constraint = getConstraint(check);
                if(null != constraint){
                    validator.addElementConstraint(elementName, constraint);
                    ///arguments = 
                    ///    new Object[]{"Element Name", elementName};      //NOI18N
                    ///System.out.println(MessageFormat.format(format, arguments));         
                    ///arguments = new Object[]{"Check", check.getName()}; //NOI18N
                    ///System.out.println(MessageFormat.format(format, arguments));
                    ///arguments = new Object[]{"Constraint", constraint}; //NOI18N
                    ///System.out.println(MessageFormat.format(format, arguments));
                    ///constraint.print();
                }
            }
        }
    }


    /**
     * Constructs <code>Validation</code> object, representing the root of
     * xml defining <code>Constraints</code> to be applied to different
     * elements.
     * Clients of this framework can specify their own file.
     * If client do not, then the default file( specified by 
     * <code>defaultValidationFile</code> ) is  used.
     */
    private void constructValidation() { 
        //Create an InpurtStream object

        URL url = null;
        InputStream inputStream = null;

        if(validationFile != null){
            inputStream = getInputStream(validationFile);
            if(inputStream == null){
                String format = 
                    BundleReader.getValue("MSG_using_the_default_file");//NOI18N
                Object[] arguments = new Object[]{defaultValidationFile};
                System.out.println(MessageFormat.format(format, arguments));

                inputStream = getDafaultStream();
            }
        } else {
            inputStream = getDafaultStream();
        }
        
        if(inputStream != null){
            //Create graph
            try {
                validation = Validation.createGraph(inputStream);
            } catch(Exception e) {
                System.out.println(e.getMessage());
                validation = null;
            }
        }
    }


    /**
     * Constructs <code>Constraint</code> object, for the given 
     * <code>check</code>. <code>check</code> object represents the 
     * information of the constraint declared in Constraints File.
     * <code>Constraints</code> to be applied to different elements are
     * declared in Validation File. This method uses Constraints File to
     * construct Constraint object from the given <code>check</code> object.
     *
     * @param check the given <code>Check</code> object
     *
     * @return <code>Constraint</code> the corresponding <code>Constraint</code>
     * object. Returns <code>null</code> in case no information for this 
     * constaint is specified in Constraints File or if Constraints File is not
     * found/defined.
     */
    private Constraint getConstraint(Check check){
        Constraint constraint = null;
        if(null == constraints) {
            constructConstraints();
        }
        CheckInfo checkInfo = null;
        if(null != constraints){
            String checkName = check.getName();
            checkInfo = getCheckInfo(checkName, constraints);
            if(null != checkInfo){
                constraint = buildConstraint(check, checkInfo);
            } else {
                String format = 
                    BundleReader.getValue("MSG_No_definition_for");     //NOI18N
                Object[] arguments = 
                    new Object[]{"CheckInfo", checkName};               //NOI18N
                System.out.println(MessageFormat.format(format, arguments));
            }
        } else { 
            String format = 
                BundleReader.getValue("MSG_Not_defined");               //NOI18N
            Object[] arguments = new Object[]{"Constraints"};           //NOI18N
            System.out.println(MessageFormat.format(format, arguments));
        }
        return constraint;
    }


    /**
     * Constructs <code>Constraints</code> object, representing the root of
     * Constraints File. Constraints File provide information about the 
     * <code>Constraint</code>s i.e. Constraint class name, Constraint 
     * constructor arguments & thier types.
     */
    private void constructConstraints() { 
        Constraints customConstraints = null;
        Constraints defaultConstraints = null;
        
        String constraintsFile =                     
            System.getProperty("constraints.file");                     //NOI18N
        ///System.out.println(consFile);
        if(constraintsFile != null){
            customConstraints = getConstraints(constraintsFile);
        }
        
        if(defaultConstraintsFile != null){
            defaultConstraints = getConstraints(defaultConstraintsFile);
        }
        
        if(customConstraints != null){
            if(defaultConstraints != null){
                int count = defaultConstraints.sizeCheckInfo();
                CheckInfo checkInfo = null;
                CheckInfo checkInfoClone = null;
                for(int i=0; i<count; i++){
                    checkInfo = defaultConstraints.getCheckInfo(i);
                    ///String str = checkInfo.dumpBeanNode();
                    checkInfoClone = (CheckInfo) checkInfo.clone();
                    ///System.out.println(checkInfoClone.dumpBeanNode());
                    customConstraints.addCheckInfo(checkInfoClone);
                }
                constraints =  customConstraints;
            } else {
                constraints =  customConstraints;                
            }
        } else {
            constraints =  defaultConstraints;
        }
    }


    /**
     * Returns the <code>CheckInfo</code> object for the given 
     * <code>Check</code> object. <code>Check</code> object represents
     * the <code>Constraint</code> object. <code>CheckInfo</code> hold
     * information for a particular <code>Constraint</code> , for example,
     * Constraint class name, Constraint constructor arguments & thier types.
     *
     * @param checkName the given <code>Check</code> name
     *
     * @return <code>CheckInfo</code> the <code>CheckInfo</code>
     * object for the given <code>Check</code>
     */
    private CheckInfo getCheckInfo(String checkName, 
                Constraints constraints){
        CheckInfo checkInfo = null;
        int size = constraints.sizeCheckInfo();
        for(int i=0; i<size; i++) {
            checkInfo = constraints.getCheckInfo(i);
            if(checkName.equals(checkInfo.getName())) {
                return checkInfo;
            }
        }
        return null;
    }


    /**
     * Constructs <code>Cosntraint</code> object, from the given
     * <code>Check</code> and <code>CheckInfo</code> objects.
     * <code>Check</code> object identifies a particular 
     * <code>Constraint</code>, whereas <code>CheckInfo</code> object
     * provides additional information about the <code>Constraint</code>,
     * such as Constraint class name, Constraint constructor arguments &
     * thier types.
     * For a given element <code>Check</code> object is constructed from
     * the information specified in Validation File whereas the
     * corresponding <code>CheckInfo</code> object is built from the 
     * infromation provided in Constraints File.
     *
     * @param check the <code>Check</code> object identifying the 
     * <code>Constraint</code>
     * @param checkInfo the given <code>CheckInfo</code> object providing more
     * information about the <code>Constraint</code> identified by input 
     * parameter <code>check</code>
     *
     * @return <code>Constraint</code> the <code>Constraint</code>object 
     * constructed from the input paramerters <code>check</code> and 
     * <code>checkInfo</code>
     */
    private Constraint buildConstraint(Check check, CheckInfo checkInfo){
        //Constraint objects are always created using the default constructor.
        //Fields in Constraint object are then set using using the setter 
        //methods. In order to set the fields in the Constraint object, field
        //values are fetched from the check object and the no of arguments,
        //argument names and thier types are fetched from checkInfo object.
        //Assumptions
        //      Every Constraint must have default constructor.
        //      Every Constraint that has fields must provide setter functions
        //      to set those fields.
        //      <name> field of <check> in Validation File is matched with 
        //      <name> field of <check-info> in Constraints File to find out
        //      infromation of a particulatar Constraint.
        //      Number of <parameters> defined in <check> should match the
        //      number of <arguments> of corresponding <check-info>
        //
        Constraint constraint = null;
        String classname = checkInfo.getClassname();
        Arguments arguments = checkInfo.getArguments();
        Class[] argumentTypeClass = new Class[1];
        Object[] argumentValue = new Object[1];
        
        String argumentName;
        String argumentType;

        constraint = (Constraint)utils.createObject(classname);
        if(null != arguments){
            int size = arguments.sizeArgument();
            Parameters parameters = check.getParameters();

            if((null != parameters) && (size == parameters.sizeParameter())){
                Argument argument =  null;
                Parameter parameter = null;
                for(int i=0; i<size; i++) {
                    argument = arguments.getArgument(i);
                    argumentName = argument.getName();
                    argumentType = argument.getType();
                    parameter = 
                        getParameter(parameters, argumentName);

                    if(parameter == null){
                        String format = BundleReader.getValue(
                            "Warning_no_value_specified_for");          //NOI18N
                        Object[] substitutes = 
                            new Object[]{argumentName, check.getName()};
                        System.out.println(
                            MessageFormat.format(format, substitutes));
                        continue;
                    }

                    if(null == argumentType){
                        argumentType = "java.lang.String";              //NOI18N
                    }

                    int noOfValues = 1;
                    if(argumentType.equals("java.lang.String[]")){      //NOI18N
                        Integer sz = (Integer)utils.getElement("value", //NOI18N
                            parameter, SIZE_PREFIX);
                        noOfValues = sz.intValue();
                        argumentType = "java.lang.String";              //NOI18N
                    } 
                    for(int j=0; j<noOfValues; j++) {
                        argumentValue[0] = utils.getElement("value",    //NOI18N
                            j, parameter);
                        argumentTypeClass[0] =
                            utils.getClass(argumentType);
                        String methodName =
                            utils.methodNameFromDtdName(argumentName,
                                SET_PREFIX);
                        Method method = 
                            utils.getMethod(utils.getClass(constraint),
                                methodName, argumentTypeClass);
                        utils.invoke(constraint, method, argumentValue);
                    }
                }
            } else {
                String format = BundleReader.getValue(
                    "MSG_Conflicting_Constraint_information");          //NOI18N
                Object[] substitues = new Object[]{check.getName()};    //NOI18N
                String message = MessageFormat.format(format, substitues);
                assert false : message;
            }
        }
        return constraint;
    }


    /**
     * Constructs <code>CardinalCosntraint</code> object, for the given
     * cardinal. Cardinal Constraint is implicit i.e user do not need to
     * define Cardinal Constraint for elements. Its applied implicitly to
     * all the elements. Where in case of other Constraints user must 
     * specify which Constraints to apply to which elements. User specifies
     * this through Validation File.
     *
     * @param cardinal the given cardinal; it could be one of the following :
     * MANDATORY_ELEMENT, MANDATORY_ARRAY, OPTIONAL_ELEMENT or OPTIONAL_ARRAY
     * 
     * @return <code>CardinalConstraint</code> object representing the given
     * cardinal
     */
    private CardinalConstraint getCardinalConstraint(int cardinal){
        Class[] argumentTypes = new Class[] {int.class};
        Constructor constructor =
            utils.getConstructor(CARDINAL_CONSTRAINT_CLASS, argumentTypes);

        Integer parameter = new Integer(cardinal);
        Object[] argumentValues = new Object[] {parameter};

        return (CardinalConstraint) utils.createObject(constructor,
            argumentValues);
    }


    /**
     * Validates the given element of the given <code>Validatee</code>.
     * This method is called for an element that is an array(optional array
     * or madatory array) of objects.
     * 
     * @param elementName the given element to be validated
     * @param validatee the given <code>Validatee</code> object; the
     * element of which needs to be validated
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    private Collection validateBeans(String elementName,
            Validatee validatee){
        //This method does an recursive call for each of the objects in an
        //array of given element.
        int noOfElements = 0;
        String sizeMethodName =  utils.methodNameFromBeanName(elementName,
            SIZE_PREFIX);
        Method sizeMethod = validatee.getMethod(sizeMethodName);
        noOfElements = ((Integer)validatee.invoke(sizeMethod)).intValue();

        ArrayList failures = new ArrayList();
        Object child = null;
        for(int i=0; i<noOfElements; i++) {
            child = validatee.getElement(elementName, i);
            if(child != null) {
                failures.addAll(validate(child));
            }
        }
        return failures;
    }


    /**
     * Validates the given element of the given <code>Validatee</code>.
     * This method is called for an element that is an object.
     * 
     * @param elementName the given element to be validated
     * @param validatee the given <code>Validatee</code> object; the
     * element of which needs to be validated
     *
     * @return <code>Collection</code> the Collection of
     * <code>ConstraintFailure</code> Objects. Collection is empty
     * if there are no failures.
     */
    private Collection validateBean(String elementName,
            Validatee validatee){
        //This method does an recursive call on the object of given element.
        ArrayList failures = new ArrayList();
        Object child = null;
        child = validatee.getElement(elementName);
        if(child != null) {
            failures.addAll(validate(child));
        }
        return failures;
    }


    /**
     * Constructs <code>InputStream</code> object, for default Validation File
     * defining <code>Constraints</code> to be applied to different
     * elements. The default Validation File is represented by the private
     * attribute <code>defaultValidationFile</code> 
     *
     * @return <code>InputStream</code> an InputStream object representing
     * default Validation File. Returns null, in case of failure.
     */
    private InputStream getDafaultStream(){
        InputStream inputStream = null;
        URL url = null;
        if(defaultValidationFile != null){
            url = utils.getUrlObject(defaultValidationFile);
            if(url != null) {
                try {
                    inputStream = url.openStream();
                } catch (IOException exception){
                    System.out.println(exception.getMessage());
                }
            } else {
                assert false : (BundleReader.getValue(
                    "Error_control_should_never_reach_here"));          //NOI18N

                String format = 
                    BundleReader.getValue("Error_can_not_access_file"); //NOI18N
                Object[] arguments = new Object[]{defaultValidationFile};
                String message = MessageFormat.format(format, arguments);
                assert false : message;
            }
        } else {
            assert false : (BundleReader.getValue(
                "Error_control_should_never_reach_here"));              //NOI18N
        }
        return inputStream;
    }


    /**
     * Gets the element from this object with the given name.
     *
     * @return <code>Parameter</code> an object with the given name.
     * Returns null if the object with the given name is not found.
     */
    private Parameter getParameter(Parameters parameters, String name){
        int size = parameters.sizeParameter();
        Parameter returnValue = null;
        Parameter parameter = null;
        String parameterName = null;
        for(int i=0; i<size; i++){
            parameter =  (Parameter)utils.getElement("parameter", i,    //NOI18N
                parameters);
            parameterName = parameter.getName();
            if(parameterName.equals(name)){
                returnValue = parameter;
                break;
            }
        }
        return returnValue;
    }


    /**
     * Constructs <code>InputStream</code> object, for the given file.
     *
     * @return <code>InputStream</code> an InputStream object representing
     * the given file. Returns null, in case of failure.
     */
    private InputStream getInputStream(String inputFile){
        //Create an InpurtStream object

        InputStream inputStream = null;

        if(inputFile.lastIndexOf(':') == -1){
            URL url = null;

            url = utils.getUrlObject(inputFile);
            if(url != null) {
                try {
                    inputStream = url.openStream();
                } catch (IOException exception){
                    System.out.println(exception.getMessage());
                }
            } else {
                String format = BundleReader.getValue(
                    "Error_specified_file_can_not_be_used");            //NOI18N
                Object[] arguments = new Object[]{inputFile};
                System.out.println(MessageFormat.format(format, arguments));
            }
        } else {
            File file = new File(inputFile);
            if(file.exists()){
                try {
                    inputStream = new FileInputStream(file);
                } catch(FileNotFoundException exception){
                    System.out.println(exception.getMessage());
                    inputStream = null;
                }
            } else {
                String format = BundleReader.getValue(
                    "Error_specified_file_can_not_be_used");            //NOI18N
                Object[] arguments = new Object[]{inputFile};
                System.out.println(MessageFormat.format(format, arguments));
            }
        }

        return inputStream;
    }


    /**
     * Gets the Validatee Implementation name for the given object from the 
     * given <code>properties</code> file.
     *
     * @return <code>String</code> name of the Validatee Implementation for the 
     * the object. Returns null, if no Validatee Implementation is found in the
     * given <code>properties</code> file.
     */
    private String getValidateeImplementation(Object object,
        String propertiesFile){
        String returnVal = null;
        Class classObject = utils.getClass(object);
        String className = classObject.getName();

        //switch the BundleReader to read from the given impl file
        //validatee implementations are specified through this impl file
        BundleReader.setBundle(propertiesFile);
        
        String validateeImplName = BundleReader.getValue(className);
        while(!(className.equals("java.lang.Object"))){                 //NOI18N
            if(!(validateeImplName.equals(className))){
                returnVal = validateeImplName;
                break;
            } else {
                classObject = classObject.getSuperclass();
                className = classObject.getName();
                validateeImplName = BundleReader.getValue(className);
            }
        }
        return returnVal;
    }


    /**
     * Constructs <code>Constraints</code> object, representing the root of
     * given Constraints File.
     * 
     * @return <code>Constraints</code> object representing the root of the
     * given Constraints File. Returns null, in case of failure.
     */
    private Constraints getConstraints(String constraintsFile){
        URL url = null;
        InputStream inputStream = null;
        Constraints constraints = null;

        if(constraintsFile != null){
            inputStream = getInputStream(constraintsFile);
        }

        //Create graph
        if(inputStream != null){
            try {
                constraints = Constraints.createGraph(inputStream);
            } catch(Exception e) {
                System.out.println(e.getMessage());
                constraints = null;
            }
        }
        return constraints;
    }
}
