/*******************************************************************************
**
** Filename:  CMIObjectiveData.java
**
** File Description:   The CMIObjectiveData class manages the actual objective
**                     data.  The objective data identifies how the student
**                     has performed on individual objectives in the
**                     SCO.
**
** Author:  ADL Technical Team
**
** Contract Number:
** Company Name: CTC
**
** Module/Package Name: org.adl.datamodel.cmi
** Module/Package Description: Collection of CMI Data Model objects
**
** Design Issues:
**        In order to use Reflection (Java Feature) the defined Java
**        coding standards are NOT being followed.  Reflection requires
**        field names to match identically to input parameter.  The
**        attribute names match what is expected from a LMSGetValue()
**        or LMSSetValue() request.  Also the attribute values are declared
**        as public scope in order to use reflection.
**
** 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
** ------------   ----------------  -------------------------------------------
** 09/22/2000     S. Thropp         PT - 111: Changed all occurrences of status
**                                  to statues to reflect changes in the CMI
**                                  Guidelines for Interoperability revision
**                                  3.0.3
**
** 10-31-2000     R. Ball           PT 293: SCORM 1.1 Changes Objectives.
**                                  modified score and status from being lists
**                                  of scores and statuses to single items.
**
** 12-14-2000     S. Thropp         PT 293: Fixed problem with an LMSGetValue()
**                                  call The processSubcategory() was always
**                                  calling performGet() on CMIScore regardless
**                                  of the element.  Added check to verify that
**                                  the request was for cmi.objectives.0.score
**                                  before calling performGet().
**
*******************************************************************************/
package org.adl.datamodels.cmi;

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

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

public class CMIObjectiveData extends CMICategory
                              implements Serializable

{
   // A developer defined, SCO specific identifier for an objective
   public Element id;

   // The score obtained by the student after each attempt to master
   // the objective
   public CMIScore score;

   // The status obtained by the student after each attempt to master
   // the objective
   public Element status;

   // Indicates if the objective data has been initialized
   private boolean initialized;

   static int CMIOBJECTIVEDATA_MIN_NUM_TOKENS = 3;

   /****************************************************************************
    **
    ** Method:  CMIObjectiveData()
    ** Input:   none
    **
    ** Output:  none
    **
    ** Description: This is the default constructor for the ObjectiveData
    **              class.  Default values are created for each element
    **
    ***************************************************************************/
   public CMIObjectiveData()
   {
        super( true );

        id = new Element("","checkIdentifier","NULL",true,true,false);
        score = new CMIScore();
        status = new Element("","checkVocabulary","Status",true,true,false);

   }


    /************************************************************************
    **  Accessers to the CMIObjectiveData.  SCOs should not call these methods.
    **  SCOs should call LMSGetValue().
    ************************************************************************/

   public Element getStatus()
   {
      return status;
   }
   public CMIScore getScore()
   {
      return score;
   }
   public Element getId()
   {
      return id;
   }

   /************************************************************************
    **  Modifiers to the CMIObjectives Data.  SCOs should not call these methods.
    **  SCOs should call LMSSetValue()
    ************************************************************************/
   public void setStatus(String inStatus)
   {
      status.setValue(inStatus);
   }
   public void setScore(CMIScore inScore)
   {
      score.getRaw().setValue( inScore.getRaw().getValue() );
      score.getMin().setValue( inScore.getMin().getValue() );
      score.getMax().setValue( inScore.getMax().getValue() );
   }
   public void setId(String inID)
   {
      id.setValue(inID);
   }


   public boolean isInitialized()
   {
      return initialized;
   }

   /****************************************************************************
    **
    ** Method:  performGet
    ** Input:   CMIRequest theRequest - the LMSGetValue() request
    **          DMErrorManager dmErrorMgr - Error manager, handles all
    **                                        error reporting
    **
    ** Output:  String - the value portion of the element for the LMSGetValue()
    **
    ** Description:  This method performs the LMSGetValue() request.  The method
    **    determines the value for the requested data element.
    **
    ***************************************************************************/
   public String performGet(CMIRequest theRequest,
                            DMErrorManager dmErrorMgr)
   {
      if ( DebugIndicator.ON )
      {
         System.out.println("CMIObjectiveData::performGet()");
      }

      // Resultant value to return
      String result = new String("");

      // Check to make sure this is a valid LMSGetValue() request
      if ( isValidObjectiveDataRequest(theRequest) )
      {
         // Get the next token off of the request
         String token = theRequest.getNextToken();

         if ( DebugIndicator.ON )
         {
            System.out.println("Token: " + token);
         }

         // Check to see if the Request has more tokens to process
         if ( theRequest.hasMoreTokensToProcess() )
         {
            // Subcategory found in request
            result = processSubcategory(theRequest,token,dmErrorMgr);
         }
         else
         {
            // No more tokes to process

            // Check to see if the request is for the children of Objective Data
            if ( theRequest.isAChildrenRequest() )
            {
               // Set result to the string of children
               result = getChildren();
            }
            else
            {
               // determine the value associated with the element requested
               result = determineElementValue(this,token,dmErrorMgr);
            }
         }
      }
      else
      {
         if ( DebugIndicator.ON )
         {
            // Error - Data Model Element not implemented
            System.out.println("Error - Data Model Element not implemented");
            System.out.println("Request being processed: " +
                               theRequest.getRequest() +
                               "\nis not a valid request for the CMI Objective\n" +
                               "Data Model Category");
         }

         dmErrorMgr.recNotImplementedError(theRequest);
      }

      // Done getting requested element.  Let the CMIRequest object
      // know that the LMSGetValue() processing is done
      theRequest.done();

      return result;
   }  // end of performGet

   /***************************************************************************
    **
    ** Method:  getChildren
    ** Input:   none
    ** Output:  String representation of the elements of the CMI Objective
    **          category
    **
    ** Description:
    **      This method returns a String containing the list of data elements
    **      that CMI Objective data object contains.
    **
    ***************************************************************************/
   public String getChildren()
   {
      String children = "id,status,score";

      return children;
   }  // end of getChildren

   /****************************************************************************
    **
    ** Method:  performSet
    ** Input:   CMIRequest theRequest - request to process
    **          DMErrorManager dmErrorMgr - Error manager
    **
    ** Output:  none
    **
    ** Description:  This method performs the LMSSetValue() request.
    **
    ***************************************************************************/
   public void performSet(CMIRequest theRequest,
                          DMErrorManager dmErrorMgr)
   {
      // Check to see if this is a valid LMSSetValue() request
      if ( isValidObjectiveDataRequest(theRequest) == true )
      {
         // Get the next token off of the request
         String token = theRequest.getNextToken();

         // Check to see if the request has any more tokens to
         // process
         if ( theRequest.hasMoreTokensToProcess() )
         {
            // More tokens to process - subcategory found
            String result = processSubcategory(theRequest,token,dmErrorMgr);
         }
         else
         {
            //  No more tokens to process

            // Get the value to use for setting off of the request
            String value = theRequest.getValue();

            // Set the value
            if ( theRequest.isAKeywordRequest() )
            {
               dmErrorMgr.recKeyWordError(token);
            }
            else
            {
               // Perform the setting of the value
               doSet(this,token,value,dmErrorMgr);
               initialized = true;
            }
         }
      }
      else
      {
         if ( DebugIndicator.ON )
         {
            // Error - Data Model Element not implemented
            System.out.println("Error - Data Model Element not implemented");
            System.out.println("Request being processed: " +
                               theRequest.getRequest() +
                               "\nis not a valid request for the CMI Objective\n" +
                               "Data Model Category");
         }

         dmErrorMgr.recNotImplementedError(theRequest);
      }

      // Done setting requested element.  Let the request object know
      // that the LMSSetValue() processing is finished
      theRequest.done();

   } // end of performSet

   /****************************************************************************
    **
    ** Method:  processSubcategory
    ** Input:   CMIRequest - the current request being processed
    **          String - the subcategory being processed
    **          DMErrorManager - the Data Model Error Manager
    **
    ** Output: String -  result to be returned.  If the request is for
    **                   an LMSSetValue() request an empty string is
    **                   returned
    **
    ** Description: This method processes the subcategories of the CMIObjectives
    **              Data Model Category.  The only valid subcategory of the
    **              CMI Objectives class is the CMIScore class.  Any other
    **              subcategories are invalid - non compliant.
    **
    **              If the request is for an LMSSetValue(), then an empty
    **              String is returned.
    **
    ***************************************************************************/
   private String processSubcategory(CMIRequest theRequest,
                                     String theElement,
                                     DMErrorManager dmErrorMgr)
   {
      if ( DebugIndicator.ON )
      {
         System.out.println("In CMIObjectiveData::processSubcategory()");
         System.out.println("The Element: " + theElement);
      }
      String result = new String("");

      try
      {
         Field tmpField = this.getClass().getField(theElement);

         // Call Get or Set on CMIScore
         if ( theRequest.isForASetRequest() )
         {
            // Set the value
            if ( theRequest.isAKeywordRequest() )
            {
               dmErrorMgr.recKeyWordError(theRequest.getElement());
            }
            else
            {
               // Invoke the performSet() on the subcategory
               score.performSet(theRequest,dmErrorMgr);
            }
         }
         else
         {
            if ( theElement.equals("score") )
            {
               result = score.performGet(theRequest,dmErrorMgr);
            }
            // Determine if the Request is for a keyword.
            else if ( theRequest.isAKeywordRequest() )
            {
               dmErrorMgr.recGetKeyWordError(theRequest.getElement());
            }
            else
            {
               dmErrorMgr.recNotImplementedError(theRequest);
            }
         }
      }
      catch ( NoSuchFieldException nsfe )
      {
         if ( DebugIndicator.ON )
         {
            // Error - Data Model Element not implemented
            // *** The only subcategory in the CMI Objective Data Category
            // *** is the score.  Any thing else is an error
            System.out.println("Error - Data Model Element not implemented");
            System.out.println("Element being processed: " + theElement +
                            "is not a valid sub category of the CMI " +
                            "Objectives\nData Model Category");
         }

         dmErrorMgr.SetCurrentErrorCode("401");
      }
      catch ( SecurityException se )
      {
         if ( DebugIndicator.ON )
         {
            System.out.println(se);
            System.out.println("Access to the information is denied");
         }
         dmErrorMgr.SetCurrentErrorCode("101");
      }

      return result;
   }

   /****************************************************************************
    **
    ** Method:  isValidObjectiveDataRequest
    ** Input:   CMIRequest theRequest - the LMSGetValue() request
    **
    ** Output:  Boolean value indicating whether or not the Request is
    **          valid.
    **
    ** Description:  Checks to make sure that the request meets the minimum
    **               requirements needed for a CMIObjectiveData Model Element.
    **
    ***************************************************************************/
   private boolean isValidObjectiveDataRequest(CMIRequest theRequest)
   {
      boolean rtrnFlag = false;

      // Check to see if the Request has the appropriate length for
      // a CMI Objective request
      if ( theRequest.getTotalNumTokens() >= CMIOBJECTIVEDATA_MIN_NUM_TOKENS )
      {
         rtrnFlag = true;
      }

      return rtrnFlag;

   } // end of isValidObjectiveDataRequest()

   /****************************************************************************
    **
    ** Method:  isValidRequest
    ** Input:   CMIRequest theRequest - tokenized request
    **          DMErrorManager dmErrorMgr - instance of the Data Model Error
    **              manager
    **
    ** Output:  boolean result indicating the validity of the request
    **
    ** Description:  This method checks to make sure the request is a valid
    **               Data Model request.
    **
    ***************************************************************************/
   public String isValidRequest(CMIRequest theRequest,
                                 DMErrorManager dmErrorMgr)
   {

      boolean flag = false;
      String result = new String("");

      if ( theRequest.getTotalNumTokens() <= 5)
      {
         // Get the next token off of the request
         String token = theRequest.getNextToken();

         if ( DebugIndicator.ON )
         {
             System.out.println("token: " + token);
         }

         if (token.equals("id") ||
             token.equals("status") )
         {
            if ( theRequest.hasMoreTokensToProcess() )
            {
               flag = false;
               dmErrorMgr.recNotImplementedError(theRequest);
            }
            else
            {
               dmErrorMgr.SetCurrentErrorCode("0");
            }
         }
         else if (token.equals("score") )
         {
             flag = score.isValidRequest(theRequest);
             if ( theRequest.isAChildrenRequest() )
             {
                 result = score.getChildren();
             }
         }
      }
      else
      {
          // More than 5 tokens element is not implemented
          dmErrorMgr.recNotImplementedError(theRequest);
      }

      return result;
   }


   /****************************************************************************
    **
    ** Method:  showData
    ** Input:   none
    ** Output: none
    **
    ** Description: Displays the current state of the CMI Objective Data object
    **
    ***************************************************************************/
   public void showData()
   {
      if ( DebugIndicator.ON )
      {
         System.out.println("CMI Objective Data Object:");

         System.out.println("\tid: " + getId().getValue());
         System.out.println("\tstatus: " + getStatus().getValue());
         System.out.println("\tscore.raw: " + getScore().getRaw().getValue());
         System.out.println("\tscore.min: " + getScore().getMin().getValue());
         System.out.println("\tscore.max: " + getScore().getMax().getValue());
      }
   }



   }  // end of CMIObjectiveData
