/*******************************************************************************
**
** Filename:  CMICategory.java
**
** File Description:   This class represents a CMI Category.  This class
**                     acts as a parent to all of the CMI Data Element
**                     Categories:
**                        CMICore, CMIScore, CMISuspendData, CMILaunchData,
**                        CMIComments, CMIObjectives, CMIObjectiveData,
**                        CMIEvaluation, CMIEvaluation, CMIEvaluationData,
**                        CMIInteractions, CMIInteractionData, CMIResponse,
**                        CMIStudentData, CMIEvaluationComments, CMIAttempt,
**                        CMITries, CMIStudentDemographics, CMIPath
**                        and CMIStudentPreference
**
** Author: ADLI Project
**
** Company Name: Concurrent Technologies Corporation
**
** Module/Package Name: org.adl.datamodel.cmi
** Module/Package Description: Collection of CMI Data Model objects
**
** Design Issues:
**
** Implementation Issues:
** Known Problems:
** Side Effects:
**
** References: AICC CMI Data Model
**             ADL SCORM
**
*******************************************************************************
**
** Concurrent Technologies Corporation (CTC) grants you ("Licensee") a non-
** exclusive, royalty free, license to use, modify and redistribute this
** software in source and binary code form, provided that i) this copyright
** notice and license appear on all copies of the software; and ii) Licensee
** does not utilize the software in a manner which is disparaging to CTC.
**
** This software is provided "AS IS," without a warranty of any kind.  ALL
** EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
** IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-
** INFRINGEMENT, ARE HEREBY EXCLUDED.  CTC AND ITS LICENSORS SHALL NOT BE LIABLE
** FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
** DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL CTC  OR ITS
** LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
** INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
** CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
** OR INABILITY TO USE SOFTWARE, EVEN IF CTC HAS BEEN ADVISED OF THE POSSIBILITY
** OF SUCH DAMAGES.
**
*******************************************************************************
**
** Date Changed   Author of Change  Reason for Changes
** ------------   ----------------  -------------------------------------------
** 11/15/2000     S.Thropp          PT 291: For performance reasons, remove
**                                  the data model validator as an attribute
**                                  and make it a local variable when needed.
**
** 11/17/2000     S.Thropp          PT 292: Changed references to old error
**                                  codes.  Codes were changed to map to the
**                                  new error codes.
**
** 01/17/2001     S.Thropp          PT 374: Added some more debug statements
**
** 03/20/2002     Bill Capone       Added a "SetCurrentErrorCode" to "0" (zero)
**                                  in doSet().
**
*******************************************************************************/
package org.adl.datamodels.cmi;

//native java imports
import java.io.*;
import java.lang.reflect.*;

//adl imports
import org.adl.util.debug.*;
import org.adl.datamodels.*;

//import from the applet which controls the writeability
import org.adl.samplerte.client.APIAdapterApplet;

public class CMICategory implements Serializable
{
   // Indicates whether or not the category can be set by an AU
   protected boolean writeable;
   
   /****************************************************************************
    **
    ** Method: Constructor
    **
    ** Input: boolean writeableFlag
    ** Output: none
    **
    ** Description:  Constructs a Data Model Category
    **
    ***************************************************************************/
   public CMICategory(boolean writeableFlag)
   {
      writeable = writeableFlag;
   }  // end of constructor


   /****************************************************************************
    **
    ** Method: isWriteable
    **
    ** Input: none
    ** Output: boolean value stating whether or not the Data Model Category
    **         is writeable
    **
    ** Description:  Returns a boolean value which indicates whether or not
    **               the Data Model Element is writeable
    **
    ***************************************************************************/
   public boolean isWriteable()
   {
      return writeable;
   }  // end of isWriteable()

   /****************************************************************************
    **
    ** Method:  determineElementValue
    ** Input:   Object - the current object (data model category)
    **          String - Data Model Element that holds the value that
    **                   is wanted
    **          DMErrorManager - The Data Model Error manager
    **
    ** Output:  String - the requested value
    **
    ** Description:  This method determines the value of the input Data model
    **               element.
    **
    ***************************************************************************/
   public String determineElementValue(Object o,
                                       String element,
                                       DMErrorManager dmErrorMgr)
   {
      // Value to return
      String rtrnString = new String("");

      // Get the Class name off of the Object
      Class c = o.getClass();
      try
      {
         // Use reflection to obtain the appropriate field (attribute)

         // Get the Field (in Class c) that matches the element
         Field tmpField = c.getField(element);

         // Get the Element object that is represented by the Field
         Element e = (Element)tmpField.get(o);

         if ( e.isReadable() )
         {
            // get the value off of the Element
            rtrnString = e.getValue();
         }
         else
         {
            if ( DebugIndicator.ON )
            {
               System.out.println("Error - Element not readable");
               System.out.println("Value: " + element + " is not readable");
            }

            // Notify error manager
            dmErrorMgr.SetCurrentErrorCode("404");
         }
      }
      catch ( NoSuchFieldException nsfe )
      {
         if (element.equals("_count") == true)
         {
            if ( DebugIndicator.ON )
            {
               System.out.println("Error - Element not an array");
               System.out.println("Element Cannont have count");
            }
            dmErrorMgr.SetCurrentErrorCode("203");
         }
         else
         {
            if ( DebugIndicator.ON )
            {
               System.out.println("Error - Invalid Argument Error");
               System.out.println("Invalid data model element: " + element);
            }

            // Notify error manager
            dmErrorMgr.SetCurrentErrorCode("201");
         }
      }
      catch ( SecurityException se )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(se);
            System.out.println("Access to the information is denied");
         }

         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");
      }
      catch ( IllegalAccessException iae )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(iae);
            System.out.println("Underlying constructor is inaccessible");
         }
         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");
      }

      return rtrnString;

   } // end of determineElementValue()

   /****************************************************************************
    **
    ** Method:  doSet
    ** Input:   Object - the current Object (data model category)
    **          String - Data Model Element that needs set
    **          String -  Value to use in the set
    **          DMErrorManager - The Data Model Error manager
    ** Output:  none
    **
    ** Description:  This method performs all of the necessary steps in order
    **               to process an LMSSetValue() request.  The method uses
    **               reflection to determine and find the element being set.
    **               The method reports any errors while processing the
    **               request.
    **
    ***************************************************************************/
   public void doSet(Object o,
                     String element,
                     String value,
                     DMErrorManager dmErrorMgr)
   {
      if ( DebugIndicator.ON )
      {
         System.out.println("CMICategory::doSet()");
         System.out.println("Object: " + o.getClass().getName());
         System.out.println("Element: " + element);
         System.out.println("Value: " + value);
      }
      // Get the class from the object passed in
      Class c = o.getClass();
      try
      {
         // Use reflection to obtain the appropriate field (attribute)
         Field tmpField = c.getField(element);

         // Get the Element object that is represented by the Field
         Element e = (Element)tmpField.get(o);

         // Check to see if the Element was implemented
         if ( e.isImplemented() )
         {
            // Check to see if the element was writeable
            if ( e.isWriteable() || APIAdapterApplet.areweloading )
            {
               // Verifiy the type of the value to be used for setting
               // matches the type of the element to be set
               if ( validateType(o,e,element,value,dmErrorMgr) )
               {
                  // set the value
                  e.setValue(value);
                  dmErrorMgr.SetCurrentErrorCode("0");
                  if ( DebugIndicator.ON )
                  {
                     System.out.println("Element: " + element +
                                     " was set to [" + value + "]");
                  }
               }
               else
               {
                  if ( DebugIndicator.ON )
                  {
                     System.out.println("*** INVALID LMSSetValue() CALL ***");
                     System.out.println("          Invalid Type            ");
                     System.out.println("Element: " + element +
                                        " was NOT set!");
                  }
                  dmErrorMgr.SetCurrentErrorCode("405");
               }
            }
            else
            {
               if ( DebugIndicator.ON )
               {
                  // Error - Data Model Element not writeable
                  System.out.println("Error - Element not Writeable");
                  System.out.println("Element: " + element +
                                  " is not writeable");
               }
               // Notify error manager
               dmErrorMgr.SetCurrentErrorCode("403");
            }
         }
         else
         {
            // Element was not implemented, check to see if it is a
            // mandatory element
            if ( e.isMandatory() )
            {
               if ( DebugIndicator.ON )
               {
                  // Error - Mandatory Data Model Element not implemented
                  System.out.println(
                     "Error - Mandatory Element not Implemented");
                  System.out.println("Mandatory Value: " + element +
                                  " was not implemented");
               }
               // Notify error manager
               dmErrorMgr.SetCurrentErrorCode("401");
            }
            else
            {
               if ( DebugIndicator.ON )
               {
                  // Not an Error - Optional element not implemented
                  System.out.println(
                     "Warning - Optional Value Not Implemented");
                  System.out.println("Optional Element: " + element +
                                  " was not implemented");
               }
               // Notify error manager
               dmErrorMgr.SetCurrentErrorCode("0");
            }
         }
      }
      catch ( NoSuchFieldException nsfe )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println("Error - Invalid Argument Error");
            System.out.println("Invalid data model element: " + element);
         }
         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("201");
      }
      catch ( SecurityException se )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(se);
            System.out.println("Access to the information is denied");
         }
         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");
      }
      catch ( IllegalAccessException iae )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(iae);
            System.out.println("Underlying constructor is inaccessible");
         }

         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");
      }

      if ( DebugIndicator.ON )
      {
         System.out.println("Exiting CMICategory::doSet()");
      }

   }  // end of doSet()


   /****************************************************************************
    **
    ** Method: validateType()
    **
    ** Input: Object o - object for which the LMSSetValue is taking place
    **        Element e -  element class being set
    **        String element - element (string representation) being set
    **        String value - value being used for set
    **        DMErrorManager dmErrorMgr -  Data Model Error Manager
    **
    ** Output: boolean value stating whether or not this is a valid LMSSetValue
    **         request
    **
    ** Description:  Returns a boolean value which indicates whether or not
    **               the LMSSetValue() request is valid.  The method
    **               uses the Data Model Validator to check to see if the
    **               value to be used for setting is the correct type for
    **               the element to be set.
    **
    ***************************************************************************/
   protected boolean validateType(Object o,
                                Element e,
                                String element,
                                String value,
                                DMErrorManager dmErrorMgr)
   {
      if ( DebugIndicator.ON )
      {
         System.out.println("   Validating data type of the set value");
         System.out.println("   Check method: " + e.getType());
         System.out.println("   Element: " + element);
         System.out.println("   Value: " + value);
      }

      boolean result = false;

      DataModelValidator dmValidator = new DataModelValidator();
      Class dmClass = dmValidator.getClass();

      // Set up the array of Class objects
      // Each element in the array corresponds to a
      // parameter of the method you want to invoke
      Class[] parameterTypes =
         new Class[] {e.getClass(), String.class};

      // Declare a variable to hold the method
      Method theMethod;

      // Declare an arry of objects for the arguments to the method
      Object[] arguments = new Object[] {e,value};
      try
      {
         // Find the method on Class c that is represented by the
         // name of the method ("performSet") and the parameter
         // types
         theMethod = dmClass.getMethod(e.getType(), parameterTypes);

         // Invoke the method that was found matching the method name
         // and parameter types
         Boolean theResult = (Boolean) theMethod.invoke(dmValidator,arguments);
         result = theResult.booleanValue();
      }
      catch ( NoSuchMethodException nsme )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(nsme);
            System.out.println("No Such method: " + e.getType() + "()");
         }

         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");

      }
      catch ( IllegalAccessException iae )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(iae);
         }
         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");
      }
      catch ( InvocationTargetException ite )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(ite);
         }
         // Notify error manager
         dmErrorMgr.SetCurrentErrorCode("101");
      }

      if ( DebugIndicator.ON )
      {
         System.out.println("   Result from validation: " + result);
      }
      return result;
   }


}  // end of CMICategory
