Tuesday, September 17, 2013

ADF BC: Handle Hijri and Gregorian Conversion

Documented by my friend  @Amr Gomaa

Entity Object Work:
-         -  Add transient attributes with name xxOutput and xxInput of Data Type ‘String’
-          - Set xxInput to ‘Mandatory’ (Optional according to the business)
-         -  Add view Accessor on StpCalendarViewObject using the defined view criteria “StpCalendarLookupCriteria”
-          - Default values”
·         For the date input transient attribute add default value expression using the defined public method with input parameter “true”: calcXX (true)
For the recalculate expression condition: select Never

·         For the date output transient attribute add default value expression using the defined public method with input parameter “false”: calcXX (false)
For the recalculate expression condition: select Never
-         - Generate EntityImpl class
·         Add the following method (which is a generic method to generate input/output fields values from DB or date default value) into your entity Impl :
    /**
     * calculates the date representation values (gregorian and hijri).
     * calculation against database date value and type.
     * @param dateAttrIndx        the date presistent attribute index
     * @param dateTypeAttrIndx    the date type presistent attribute index
     * @param isInputDate         true if the data to be returned is for input date field, false for output date field
     * @return                    return the requested date.
     */
    private String calculateDate(Integer dateAttrIndx,Integer dateTypeAttrIndx, boolean isInputDate){
        String retVal = null;
        //for non nul date in database
        if(getAttribute(dateAttrIndx) != null){
            //create date conversion utility instance with stp_calendar view accessor
            DateTypeConversionUtil dtcu = new DateTypeConversionUtil(getStpCalendarViewAccessor());
            //convert date according to database date value and type
            dtcu.convertDate((Timestamp)getAttribute(dateAttrIndx),(String)getAttribute(dateTypeAttrIndx));
            //check if the date to be returned is the input date or output date
            if(isInputDate){
                //return input date string
                retVal = dtcu.getInputDateString();
            }else{
                //return output date string
                retVal = dtcu.getOutputDateString();
            }
        }
        return retVal;
    }

·         Add the following public  method :
    public String calcXX(boolean isInputDate){
        return calculateDate(TERMDEFINITIONDATE,TERMDEFINITIONDATETYPE,isInputDate);
    }

Where the XX represents the date field name.

·         In the create method of the entity Impl (after super.create() ) add the following 2 lines of code:
        setAttributeInternal([index of transient attribute for date input],calcXX(true));
        setAttributeInternal([index of transient attribute for date output],calcXX(false));


·         In the setter of date input attribute (setXXInput) add the following code:

        DateTypeConversionUtil dtcu = new DateTypeConversionUtil(getStpCalendarViewAccessor());
        try {
            dtcu.convertDate(value);
            value = dtcu.getInputDateString();
            setXXOutput(dtcu.getOutputDateString());
            setXXType(dtcu.getDateType());
            setXX (dtcu.getGregorianDate());
        } catch (Exception e) {
            value = "?" + e.getMessage();
        }

Notes:
·         XX : represents the common name for dates attributes,
         e.g. if the main attribute name is “TermDefinitionDate = XX“,
then we have (setTermDefinitionDate, setTermDefinitionDateInput ,setTermDefinitionDateOutput, setTermDefinitionDateType).
                 Representing (main date attribute as Timestamp, the input date attribute string,the converted output date string, main date type “H” or “G”) respectively.
·         This code to be inserted in the setter before “setAttributeInternal”


-          - Add attribute validation for the date input  transient attribute as following:
·         In the business logic unit of the entity, override the date input transient attribute.
·         Add “Script expression” validation to that attribute with the following script:
if(newValue != null)
{
  return !newValue.startsWith("?")
}else{
  return true
}
·         In the failure handling, select the failure message from the resource bundle with the following key:
com.ejada.model.entities.rules.common.DateInput_Invalid
·         Finally you will find a token message defined below, add the following expression to define its expression:
newValue.substring(1)


View Object Work:
-          Add the input and output date attributes from entity to the corresponding VO

UI Page:
-          Use the two attributes in the UI display page (the one for date input “inputText with LabeL and auto-submit” , date other one for the converted output date “inputText without label and disabled“).
  
Notes:
-          Finally Test your work with , the valid date input with the following constrains:
·          “ddmmyyy” , dd<=31 , mm<=12,
·         No separators are added between digits.
·         Day and month should be represented by two digits (01,11, .. etc)
·         Valid Gregorian date after that exists within Stp_calendar table ranging, or valid hijri date according to Stp_Calendar table.




Attachment:
- StpCalendarViewObject is a read only view object get date mapping from the table in database.
<ViewObject  xmlns="http://xmlns.oracle.com/bc4j" Name="StpCalendarLookup" Version="11.1.2.62.76"
  BindingStyle="OracleName" CustomQuery="true" FetchMode="FETCH_AS_NEEDED" PageIterMode="Full" UseGlueCode="false">
  <DesignTime>
    <Attr Name="_isExpertMode" Value="true"/>
  </DesignTime>
  <Properties>
    <SchemaBasedProperties>
      <LABEL  ResId="com.ejada.common.queries.StpCalendarLookup_LABEL"/>
    </SchemaBasedProperties>
  </Properties>
  <Variable  Name="inputGregorianDate" Kind="viewcriteria" Type="java.sql.Timestamp"/>
  <Variable Name="inputHijriYear" Kind="viewcriteria" Type="java.lang.String"/>
  <SQLQuery><![CDATA[SELECT  STP_CALENDAR.H_YR H_YR, STP_CALENDAR.STRT_G_DT STRT_G_DT, STP_CALENDAR.END_G_DT END_G_DT,  STP_CALENDAR.L1 L1, STP_CALENDAR.L2 L2, STP_CALENDAR.L3 L3, STP_CALENDAR.L4 L4,  STP_CALENDAR.L5 L5, STP_CALENDAR.L6 L6, STP_CALENDAR.L7 L7,  STP_CALENDAR.L8 L8,  STP_CALENDAR.L9 L9, STP_CALENDAR.L10 L10,  STP_CALENDAR.L11 L11,  STP_CALENDAR.L12 L12 FROM STP_CALENDAR]]></SQLQuery>
  <ViewAttribute Name="HYr" IsUpdateable="false" IsPersistent="false"  PrecisionRule="true" Precision="255" Type="java.lang.String" ColumnType="VARCHAR2"  AliasName="H_YR" Expression="H_YR"  SQLType="VARCHAR">
    <DesignTime>
      <Attr Name="_DisplaySize" Value="4"/>
    </DesignTime>
  </ViewAttribute>
  <ViewAttribute Name="StrtGDt" IsUpdateable="false" IsPersistent="false"  PrecisionRule="true" Type="java.sql.Timestamp"  ColumnType="DATE"  AliasName="STRT_G_DT"  Expression="STRT_G_DT"  SQLType="DATE"/>
  <ViewAttribute  Name="EndGDt"   IsUpdateable="false"  IsPersistent="false"  PrecisionRule="true"  Type="java.sql.Timestamp" ColumnType="DATE"  AliasName="END_G_DT" Expression="END_G_DT"  SQLType="DATE"/>
  <ViewAttribute  Name="L1"  IsUpdateable="false"  IsPersistent="false"  PrecisionRule="true" Precision="2" Scale="0" Type="java.lang.Integer" ColumnType="NUMBER"  AliasName="L1"  Expression="L1" SQLType="INTEGER"/>
  <ViewAttribute Name="L2"  IsUpdateable="false"  IsPersistent="false" PrecisionRule="true" Precision="2"  Scale="0"  Type="java.lang.Integer" ColumnType="NUMBER"  AliasName="L2" Expression="L2"  SQLType="INTEGER"/>
  <ViewAttribute  Name="L3"  IsUpdateable="false"  IsPersistent="false"  PrecisionRule="true"  Precision="2"   Scale="0" Type="java.lang.Integer" ColumnType="NUMBER" AliasName="L3"  Expression="L3" SQLType="INTEGER"/>
  <ViewAttribute  Name="L4"   IsUpdateable="false" IsPersistent="false"  PrecisionRule="true"   Precision="2" Scale="0" Type="java.lang.Integer"  ColumnType="NUMBER"  AliasName="L4"   Expression="L4" SQLType="INTEGER"/>
  <ViewAttribute  Name="L5"   IsUpdateable="false"   IsPersistent="false"  PrecisionRule="true" Precision="2" Scale="0" Type="java.lang.Integer" ColumnType="NUMBER"  AliasName="L5" Expression="L5"  SQLType="INTEGER"/>
  <ViewAttribute Name="L6"  IsUpdateable="false"  IsPersistent="false"  PrecisionRule="true"  Precision="2"  Scale="0" Type="java.lang.Integer" ColumnType="NUMBER"  AliasName="L6" Expression="L6" SQLType="INTEGER"/>
  <ViewAttribute Name="L7"  IsUpdateable="false"  IsPersistent="false"  PrecisionRule="true" Precision="2" Scale="0" Type="java.lang.Integer" ColumnType="NUMBER" AliasName="L7" Expression="L7" SQLType="INTEGER"/>
  <ViewAttribute  Name="L8" IsUpdateable="false"  IsPersistent="false" PrecisionRule="true" Precision="2"  Scale="0" Type="java.lang.Integer" ColumnType="NUMBER" AliasName="L8"  Expression="L8" SQLType="INTEGER"/>
  <ViewAttribute Name="L9" IsUpdateable="false" IsPersistent="false" PrecisionRule="true" Precision="2" Scale="0" Type="java.lang.Integer"  ColumnType="NUMBER" AliasName="L9" Expression="L9" SQLType="INTEGER"/>
  <ViewAttribute Name="L10" IsUpdateable="false" IsPersistent="false" PrecisionRule="true" Precision="2"  Scale="0" Type="java.lang.Integer"  ColumnType="NUMBER" AliasName="L10" Expression="L10" SQLType="INTEGER"/>
  <ViewAttribute Name="L11" IsUpdateable="false" IsPersistent="false" PrecisionRule="true" Precision="2" Scale="0" Type="java.lang.Integer"  ColumnType="NUMBER" AliasName="L11" Expression="L11" SQLType="INTEGER"/>
  <ViewAttribute  Name="L12"  IsUpdateable="false" IsPersistent="false"  PrecisionRule="true"  Precision="2" Scale="0" Type="java.lang.Integer" ColumnType="NUMBER"  AliasName="L12" Expression="L12" SQLType="INTEGER"/>
  <ViewCriteria  Name="StpCalendarLookupCriteria" ViewObjectName="com.ejada.common.queries.StpCalendarLookup" Conjunction="AND">
    <Properties>
      <CustomProperties>
        <Property Name="displayOperators" Value="InAdvancedMode"/>
        <Property Name="autoExecute" Value="false"/>
        <Property Name="allowConjunctionOverride" Value="true"/>
        <Property Name="showInList" Value="true"/>
        <Property  Name="mode" Value="Basic"/>
      </CustomProperties>
    </Properties>
    <ViewCriteriaRow  Name="StpCalendarLookupCriteria_row_0"  UpperColumns="1">
      <ViewCriteriaItem Name="StrtGDt" ViewAttribute="StrtGDt" Operator="ONORBEFORE" Conjunction="AND" Value=":inputGregorianDate"  IsBindVarValue="true"  Required="Optional"/>
      <ViewCriteriaItem  Name="StpCalendarLookupCriteria_StpCalendarLookupCriteria_row_0_EndGDt"  ViewAttribute="EndGDt" Operator="ONORAFTER" Conjunction="AND" Value=":inputGregorianDate" IsBindVarValue="true" Required="Optional"/>
    </ViewCriteriaRow>
    <ViewCriteriaRow Name="StpCalendarLookupCriteria_row_1"  UpperColumns="1">
      <ViewCriteriaItem  Name="HYr"    ViewAttribute="HYr"   Operator="="   Conjunction="AND"  Value=":inputHijriYear"     IsBindVarValue="true"  Required="Optional"/>
    </ViewCriteriaRow>
  </ViewCriteria>
  <ResourceBundle>
    <PropertiesBundle  PropertiesFile="com.ejada.EjadaGRPServiceBundle"/>
  </ResourceBundle>
</ViewObject>

  - DateTypeConversionUtil is a class contain code to convert the date between Hijri and Gregorian
import oracle.jbo.RowSet;
import oracle.jbo.domain.Timestamp;
import oracle.jbo.server.ViewRowImpl;

/**
 * The <code>DateTypeConversionUtil</code> class validates input date as string input.
 * Then it converts the input date from Gregorian to Hijri and vice versa using Stp_Calendar view accessor.
 *
 * @author Amr Gomaa
 */
public class DateTypeConversionUtil {
    /** Integers that represents the input date as array of : year, month and day  */
    private Integer[] inputDate;
    /** Integers that represents the output date as array of : year, month and day */
    private Integer[] outputDate;
    /** Integers that represents the gregorian start date as array of : year, month and day */
    private Integer[] gregorianStartDate;
    /** Integers that represents the gregorian end date as array of : year, month and day */
    private Integer[] gregorianEndDate;
    /** Representing input date type*/
    private int dateType;
    /** View Accessor on the view object that queries on hijri Calendar database table (Stp_Calendar)*/
    private RowSet hijriCalendarViewAccessor;
    /** stp_calendar row that represents the input date hijri year.*/
    private ViewRowImpl inputDateHijriYear;
    /** variable that represents the gregorian date as a timestamp*/
    private Timestamp gregorianDate;
    
    /** Constant representing year field index*/
    private final int YEAR = 0;
    /** Constant representing month field index*/
    private final int MONTH = 1;
    /** Constant representing day of month field index*/
    private final int DAY = 2;
    /** Date validation format regex strings, ddmmyyy where:
     * dayFormat : 01 --> 31
     * monthFormat : 01 --> 12
     * yearFormat : any four digits.
     */
    /** Constant representing day format string regex*/
    private final String dayFormat = "([0][1-9]|[1-2][0-9]|[3][0-1])";
    /** Constant representing month format string regex*/
    private final String monthFormat = "([0][1-9]|[1][0-2])";
    /** Constant representing year format string regex*/
    private final String yearFormat = "([0-9]{4})";
    /** Constant represneting Gregorian date type */
    private final int GREGORIAN_DATE = 3;
    /** Constant represneting Hijri date type */
     private final int HIJRI_DATE = 4;

    
    /**
     * Constructor with input of stp_calendar view accessor for hijri date validation and conversion.
     * @param hijriCalendarViewAccessor   stp_calendar view accessor
     */
    public DateTypeConversionUtil(RowSet hijriCalendarViewAccessor) {
        //store the input parameter into internal variable.
        this.hijriCalendarViewAccessor = hijriCalendarViewAccessor;
        //initialize input date array
        this.inputDate = new Integer[3];
    }
    
    /**
     * Convert input date from one type to another (Hijri to Gregorian and vice versa)
     * First validates the input date (Format and value).
     * Then convert date.
     * @param inputDateStr  input date string.
     * @return              returns true if conversion done successfully, false otherwise
     * @throws Exception    Throws Exception if input date string validation fails.
     */
    public boolean convertDate(String inputDateStr)throws Exception{
        boolean retVal = false;
        //validate input date formate and value.
        retVal = validateInputDate(inputDateStr);
        //convert input date into output date if validation passed.
        if(retVal){
            outputDate = convertInputDateToOutputDate();
        }
        return retVal;
    }
    
    /**
     * Converts database gregorian date into hijri date, and store them into input & output dates 
     * according to date type:
     * Date type hijri "H" : input date hijri & output date gregorian
     * Date type gregorian "G" : input date gregorian & output date hijri
     * @param gregorianDateTs  gregorian date from database.
     * @param dateTypeStr      type of input date saved in database
     */
    public void convertDate(Timestamp gregorianDateTs,String dateTypeStr){
        try{
            inputDateHijriYear = getHijriYearData("inputGregorianDate", gregorianDateTs);
        }catch(Exception e){
            e.printStackTrace();
        }
        if(dateTypeStr.trim().contentEquals("H")){
            dateType = HIJRI_DATE;
            outputDate = parseGregorianDate(gregorianDateTs);
            inputDate = convertGregorianToHijri(outputDate);
        }else if(dateTypeStr.trim().contentEquals("G")){
            dateType = GREGORIAN_DATE;
            inputDate = parseGregorianDate(gregorianDateTs);
            outputDate = convertGregorianToHijri(inputDate);
        }
    }
    
    /**
     * Validates input date string formate and value
     * @param inputDateStr    input date string to be checked
     * @return             returns true if the input date  format is ok && the input date is a valid date,
     *                     returns false otherwise.
     * @throws Exception   Throws Exception if the input date validation fails.
     */
    private boolean validateInputDate(String inputDateStr)throws Exception{
        boolean retVal = false;
        //Validate input date string format
        retVal = validateInputDateFormat(inputDateStr);
        //check if input date string format is valid
        if(retVal){
            //Vaidate if input date is a valid date
            retVal = validateInputDateValue(inputDateStr);
            //check if input date is valid date
        }
        return retVal;
    }
    
    /**
     * This mehtod is used to validate the input date  string format.
     * @param inputDateStr    Input date string that to be validated.
     * @return             returns true if the date format validation pasess, flase otherwise
     * @throws Exception   Throws Exception if the input date string format is wrong.
     */
    private boolean validateInputDateFormat(String inputDateStr)throws Exception{
        boolean retVal = false;
        // 1.Valiate input date length, should be 8 digits
        if(inputDateStr.matches("([0-9]{8})")){
            // 2.Validate date format as : ddmmyyyy
            if(inputDateStr.matches(dayFormat+monthFormat+yearFormat)){
                //date formate validation ok.
                retVal = true;
            }else{
                throw new Exception("Wrong Date Format");
                //You can make it return false otherwise
            }
        }else{
            throw new Exception("Wrong Date Format");
            //You can make it return false otherwise
        }
        return retVal;
    }
    
    /**
     * Parses the input string into dat, month, year variables.
     * Checks validity of the input date value (as Hijri or Gregorian).
     * @param inputDateStr     Input date string that to be validated
     * @return              returns true if the date value validation pasess, false otherwise
     * @throws Exception    Throws exception if the input date value is not valid
     */
    private boolean validateInputDateValue(String inputDateStr)throws Exception{
        boolean retVal = false;
        //parse input date string into three integers representing : year, month, day.
        parseInputDateElements(inputDateStr);
        //parse the type of the input date
        parseInputDateType();
        //validate input date value if it is within the valid dates as Gregorian or Hijri.
        retVal = validateDateValue();
        return retVal;
    }
    
    /**
     * Checks validity of the date value (as Hijri or Gregorian).
     * @return             returns true if the date value validation pasess, false otherwise
     * @throws Exception   Throws exception if the date value is not valid
     */
    private boolean validateDateValue()throws Exception{
        boolean retVal = false;
        //check the input date type to be validated if Gregorian or Hijri.
        if(dateType == GREGORIAN_DATE){
            //validate gregorian date.
            retVal = validateGregorianDate();
        }else if(dateType == HIJRI_DATE){
            //validate hijri date.
            retVal = validateHijriDate();
        }
        return retVal;
    }
    
    /**
     * this method validates gregorian date with consideration of month length and leap year as following:
     * input day should be <= month length    where:
     * month length = 30 + extendedMonthFlag - februaryFlag
     * extendedMonthFlag: this flag is "1" for "31 days" months, and "0" otherwise.
     * februaryFlag: for input month 2 (February) --> is "1" for leap year, and "2" for non leap year
     *               months other than 2 this flag is "0"
     * @return              returns true if the gregorian date value validation pasess, false otherwise
     * @throws Exception    Throws exception if the gregorian date value is not valid
     */
    private boolean validateGregorianDate()throws Exception{
        boolean retVal = false;
        //check if input day is not greater than month length.
        if(inputDate[DAY] > getGregorianMonthLengh(inputDate[MONTH],inputDate[YEAR])){
            throw new Exception("Wrong input date " + inputDate[DAY]+"-" + inputDate[MONTH]+"-" + inputDate[YEAR]);
        }else{
            //Store the input gregorian date as Timestamp with format "yyyy-mm-dd"
            gregorianDate = Timestamp.toTimestamp(getDateTimestampFormat(inputDate));
            //get the stp_calendar row representing the input date equivalent hijri year
            inputDateHijriYear = getHijriYearData("inputGregorianDate",gregorianDate);
            //check if the input gregorian date in valid in stp_calendar.
            if(inputDateHijriYear != null){
                //gregorian date value validation ok.
                retVal = true;
            }
        }
        return retVal;
    }
    
    /**
     * this method validates hijri date using view accessor that queries on hijri calendar database table.
     * 
     * @return              returns true if the hijri date value validation pasess, false otherwise
     * @throws Exception    Throws exception if the hijri date value is not valid
     */
    private boolean validateHijriDate()throws Exception{
        boolean retVal = false;
        //variable represents input month length
        Integer monthLength = 0;
        //get the stp_calendar row representing the input date equivalent hijri year
        inputDateHijriYear = getHijriYearData("inputHijriYear",inputDate[YEAR].toString());
        //get input month length from the retrived stp_calendar row.
        monthLength = (Integer)inputDateHijriYear.getAttribute("L"+inputDate[MONTH].toString());
        //check if input day is greater than month length as defined in the stp_calendar
        if(inputDate[DAY] > monthLength){
            //if input day is greater than month length as defined in the stp_calendar, then this is invalid date.
            throw new Exception("Wrong input date " + inputDate[DAY]+"-" + inputDate[MONTH]+"-" + inputDate[YEAR]);
        }else{
            //hijri date value validation ok.
            retVal = true;
        }
        return retVal;
    }
    
    /**
     * This method is used to parse the input date string into the three representing Integers:
     * Year , Month , Day
     * @param inputDateStr  Input date string that to be parsed
     */
    private void parseInputDateElements(String inputDateStr){
        //1.Parse day 2 digits (1st & 2nd chars) into inputDate[DAY] variable.
        String data = inputDateStr.substring(0, 2);
        inputDate[DAY] = Integer.parseInt(data);
        //2.Parse Month 2 digits (3rd & 4th chars) into inputDate[MONTH] variable.
        data = inputDateStr.substring(2, 4);
        inputDate[MONTH] = Integer.parseInt(data);
        //3.Parse Year 4 digits (5th to 8th chars) into inputDate[YEAR] variable.
        data = inputDateStr.substring(4);
        inputDate[YEAR] = Integer.parseInt(data);
    }
    
    /**
     * This method is used to parse the input date type.
     */
    private void parseInputDateType(){
        //check the input year against a specific value "1800"
        if(inputDate[YEAR] < 1800){
            //If the input year < 1800 then it's Hijri date
            dateType = HIJRI_DATE;
        }else{
            //If the input year > 1800 then it's Gregorian date
            dateType = GREGORIAN_DATE;
        }
    }
    
    /**
     * Converts the gregorian date from Timestamp type into array of Integers : year, month, day
     * @param gregorianTimestamp    Gregorian date in Timestamp format
     * @return                      Gregorian date as array of integers : year , month , day.
     */
    private Integer[] parseGregorianDate(Timestamp gregorianTimestamp){
        //get the date string of the input timestamp
        String dateStr = gregorianTimestamp.toString().split("\\s")[0];
        //seperate each element of the date in an index of string array.
        String[] dateElements = dateStr.split("-");
        //initialize array of integers to parse date elements into it
        Integer[] date = new Integer[3];
        //parse year string into integer array.
        date[YEAR] = Integer.parseInt(dateElements[YEAR]);
        //parse month string into integer array.
        date[MONTH] = Integer.parseInt(dateElements[MONTH]);
        //parse day string into integer array.
        date[DAY] = Integer.parseInt(dateElements[DAY]);
        return date;
    }
    
    /**
     * Returns date type as string:
     * "G" : stands for Gregorian date type.
     * "H" : stands for Hijri date type.
     * @return   date type string.
     */
    public String getDateType(){
        //initize type to "G" gregorian date type
        String dateTypeStr = "G";
        //check if date type is Hijri
        if(dateType == HIJRI_DATE){
            //if date type is Hijri set date type to "H" hijri date type
            dateTypeStr = "H";
        }
        return dateTypeStr;
    }
    
    /**
     * converts input date into output date of the other type
     * i.e. if the input date is Gregorian, then the output date is Hijri and vice versa.
     */
    /**
     * converts input date into output date of the other type
     * i.e. if the input date is Gregorian, then the output date is Hijri and vice versa.
     * @return   the converted output date.
     */
    private Integer[] convertInputDateToOutputDate(){
        Integer[] tempOutputDate = null;
        //check the input date type
        //For gregorian input date type 
        if(dateType == GREGORIAN_DATE){
            //convert gregorian input date into hijri output date
            tempOutputDate = convertGregorianToHijri(inputDate);
        //For gregorian input date type 
        }else if(dateType == HIJRI_DATE){
            //convert hijri input date into gregorian output date
            tempOutputDate = convertHijriToGregorian(inputDate);
        }
        return tempOutputDate;
    }
    
    /**
     * converts gregorian input date into hijri output date.
     * @param gregorianDate   the gregorian date to be converted to hijri date.
     * @return   hijri date.
     */
    private Integer[] convertGregorianToHijri(Integer[] gregorianDate){
        Integer[] hijriDate = new Integer[3];
        /**
         * The following variable holds the total number of days from the start of the input's hijri year
         * till the specified input date.
         * It is initialized in the follwing way:
         * - Add the input date day of month since it represents the days that passed of this month.
         *   (that number will include the first day of year as well and days of the previous hijri year
         *   in case of the input date month is the same as the start date month).
         * - Subtract the start date day of month since it represents the number of days that are not included 
         *   in this hijri year (that will include subtracting the first day of the year as well which to be
         *   compensated with the next element "1").
         * - Adding "1" to number of days that represents The first day of the year to compensate the subtracted
         *   day within the start date day of month.
         */
        int totalHijriYearDays = gregorianDate[DAY] - gregorianStartDate[DAY] + 1;
        /**
         * This variable represents a cursor that iterates over the gregorian monthes from the start date month
         * till the end date month.
         * It is initialized with the start date month.
         */
        int nextGregorianMonthCursor = gregorianStartDate[MONTH];
        
        /**
         * This variable represents a cursor that iterates over the gregorian years from the start date year.
         * It is initialize with the gregorian start date year
         */
        int gregorianYearCursor = gregorianStartDate[YEAR];
        
        /**
         * This loop iterates over monthes from start month till the input date month to calculate 
         * total number of days
         * The following loop starts with the following condition:
         * If the month cursor is pointing to a month that's the same as the input date month
         * and the input date year is the same as the start date year, then break.
         * Otherwise continute total days calculation.
         */
        while(!(nextGregorianMonthCursor == gregorianDate[MONTH] && gregorianYearCursor == gregorianDate[YEAR])){
            /**
             * Add this month's length (number of days) to the total days to get the remaining total days.
             * In the case that the input date month is not the same as the start date month:
             * - The addition of start date month length into total days beside the subtracted start date day
             *   gives the remaining days on the month after the start date day.
             */
            totalHijriYearDays = totalHijriYearDays + getGregorianMonthLengh(nextGregorianMonthCursor,gregorianDate[YEAR]);
            //Move the cursor to the next month
            //check if cursor over month "12" which is the last month of the year.
            if(nextGregorianMonthCursor < 12){
                //case that the cursor is over a month < 12, then move the cursor to the next month
                nextGregorianMonthCursor++;
            }else{
                //case that the cursor is over the month "12" which is the last month, then move to first 
                //month of the next year.
                nextGregorianMonthCursor = 1;
                gregorianYearCursor++;
            }
        }
        
        /**
         * This variable represents a cursor that iterates over the hijri monthes from the first month
         * till the end date month.
         * It is initialized with the first month.
         */
        int nextHijriMonthCursor = 1;
        
        /**
         * This loop iterates over hijri monthes as long as the current iteration month's length is < total days
         * Then it subtracts the monthes lengths till we get total days <= current cursor month length
         * Current cursor is over the output date month, and the remaining total days represents 
         * the output day of month.
         */
        while(totalHijriYearDays > (Integer)inputDateHijriYear.getAttribute("L" + nextHijriMonthCursor)){
            //subtract months length from total days to get the remaining days after that month.
            totalHijriYearDays = totalHijriYearDays - (Integer)inputDateHijriYear.getAttribute("L" + nextHijriMonthCursor);
            //Move cursor to the next month.
            nextHijriMonthCursor++;
        }
        
        //store the hijri date in the output date array.
        //store the hijri year represented by the year in the stp_calendar row.
        hijriDate[YEAR] = Integer.parseInt((String)inputDateHijriYear.getAttribute("HYr"));
        //store the hijri month represented by the month on which the cursor is.
        hijriDate[MONTH] = nextHijriMonthCursor;
        //store the hijri day of month represented by the remaining total days.
        hijriDate[DAY] = totalHijriYearDays;
        return hijriDate;
    }
    
    /**
     * converts hijri input date into gregorian output date
     */
    /**
     * converts hijri input date into gregorian output date
     * @param hijriDate    hijri date to be converted into gregorian date
     * @return       the gregorian date.
     */
    private Integer[] convertHijriToGregorian(Integer[] hijriDate){
        Integer[] tempGregorianDate = new Integer[3];
        /**
         * The following variable holds the total number of days from the start of the input's hijri year
         * till the specified input date.
         * It is initialized with the input date day of month that represents the number of days passed
         * of the input date month.
         */
        int totalHijriYearDays = hijriDate[DAY];
        /**
         * This variable represents a cursor that iterates over the hijri monthes from the first month
         * till the input date month.
         * It is initialized with the first month.
         */
        int nextHijriMonthCursor = 1;
        /**
         * This loop iterates over hijri monthes as long as the current iteration month is not equal to the
         * input date month.
         * If the month cursor is pointing to a month that's the same as the input date month, then break.
         * Otherwise continute total days calculation.
         */
        while(nextHijriMonthCursor != hijriDate[MONTH]){
            //add months length to total days to get.
            totalHijriYearDays = totalHijriYearDays + (Integer)inputDateHijriYear.getAttribute("L" + nextHijriMonthCursor);
            //Move cursor to the next month.
            nextHijriMonthCursor++;
        }
        
        /**
         * This variable represents gregorian day of month.
         * It is initialized to get the input gregorian day of month using the total days passed of the year:
         * - Add calculated total days to the gregorian start date day of month.
         * - Subtract "1" from since the first day of the year is included twice :
         *   First : by the start date day of month.
         *   Second : by the total days number.
         * 
         * Notes:
         * - If the input date month is the same as the start date month withing the same year, then
         *   this variable will actually represent the gregorian day of month.
         * - If the input date month is different from the start date month or they're not within the same year,
         *   then the start date month length is subtracted to compensate the added value of the start date 
         *   day of month
         */
        int gregorianDayOfMonth = gregorianStartDate[DAY] + totalHijriYearDays - 1;
            
        /**
         * This variable represents a cursor that iterates over the gregorian monthes from the start date month
         * till the end date month.
         * It is initialized with the start date month.
         */
        int nextGregorianMonthCursor = gregorianStartDate[MONTH];
        
        /**
         * This variable represents a cursor that iterates over the gregorian years from the start date year.
         * It is initialize with the gregorian start date year
         */
        int gregorianYearCursor = gregorianStartDate[YEAR];
        
        /**
         * This loop iterates over gregorian monthes as long as :
         * the current iteration month's length is < gregorian day of month.
         * Then it subtracts the monthes lengths till we get gregorian day of month <= current cursor month length
         * Current cursor is over the output date month, and the remaining gregorian day of month represents 
         * the output day of month.
         */
        while(gregorianDayOfMonth > getGregorianMonthLengh(nextGregorianMonthCursor,hijriDate[YEAR])){
            //subtract months length from total days to get the remaining day of month after that month.
            gregorianDayOfMonth = gregorianDayOfMonth - getGregorianMonthLengh(nextGregorianMonthCursor,hijriDate[YEAR]);
            //Move the cursor to the next month
            //check if cursor over month "12" which is the last month of the year.
            if(nextGregorianMonthCursor < 12){
                //case that the cursor is over a month < 12, then move the cursor to the next month
                nextGregorianMonthCursor++;
            }else{
                //case that the cursor is over the month "12" which is the last month, then move to first 
                //month of the next year.
                nextGregorianMonthCursor = 1;
                //move the output date year to the neaxt year.
                gregorianYearCursor++;
                
            }
        }
        
        //store the hijri date in the output date array.
        //store the gregorian year represented by the gregorian year cursor.
        tempGregorianDate[YEAR] = gregorianYearCursor;
        //store the gregorian month represented by the month on which the cursor is.
        tempGregorianDate[MONTH] = nextGregorianMonthCursor;
        //store the gregorian day of month represented by the gregorian day of month.
        tempGregorianDate[DAY] = gregorianDayOfMonth;
        return tempGregorianDate;
    }
    
    /**
     * returns stp_calendar row that represents the input date equivalent hijri year
     * @param bindVarName  bind variable parameter to be set in the view accessor criteria.
     * @param value        the value of the bind variable
     * @return             stp_calendar row that represents the input date equivalent hijri year
     * @throws Exception   Throws Exception if there is no representation in stp_calendar for the input date.
     */
    private ViewRowImpl getHijriYearData(String bindVarName,Object value)throws Exception{
        ViewRowImpl stpRow = null;
        //Modify criteria of the view accessor to retrieve Str-Calendar row that represents the input year.
        hijriCalendarViewAccessor.setNamedWhereClauseParam(bindVarName, value);
        //execute the new query to have the new Rowset
        hijriCalendarViewAccessor.executeQuery();
        //check if the rowset is not empty, 
        if(hijriCalendarViewAccessor.hasNext()){
            //get the stp_calendar row representing the input year
            stpRow= (ViewRowImpl)hijriCalendarViewAccessor.next();
            //get gregorian start date as array of integers
            gregorianStartDate = parseGregorianDate(new Timestamp((java.sql.Timestamp)stpRow.getAttribute("StrtGDt")));
            //get gregorian end date as array of integers
            gregorianEndDate = parseGregorianDate(new Timestamp((java.sql.Timestamp)stpRow.getAttribute("EndGDt")));
        //If rowset empty, then this hijri year does not exist in stp_calendar (i.e. invalid date)
        }else{
            throw new Exception("Wrong input date, date out of range");
        }
        return stpRow;
    }
    
    /**
     * Returns the calculated length of a given gregorian month in a given year..
     * @param month  gregorian month
     * @param year   gregorian year whose month length to be calculated
     * @return       gregorian month length
     */
    private int getGregorianMonthLengh(int month,Integer year){
        //this flag is "1" for leap year, and "2" for non leap year
        int februaryFlag = 0;
        //this flag is "1" for "31 days" months, and "0" for "30 days" months.
        int extendedMonthFlag = 0;
        //variable represents input month length
        int monthLength = 0;
        
        //Case input month is February whose length depends on leap years.
        if(month == 2){
            //set february flag to "2" as for non leap year.
            februaryFlag = 2;
            if(year % 4 == 0){
                //deacrese february flag by one for leap year to be "1".
                februaryFlag--;
            }
        // Case for months that are 31 days (1,3,5,7,8,10,12)
        }else if((month / 2 < 4 && month % 2 != 0 ) || (month / 2 >= 4 && month % 2 == 0 )){
            //increase extended month flag to "1"
            extendedMonthFlag++;
        }
        //calculate month length.
        //For February non leap year : 30 + 0 - 1 = 28
        //For February leap year : 30 + 0 - 2 = 29
        //For months (1,3,5,7,8,10,12) : 30 + 1 - 0 = 31
        //For months (4,6,9,11) : 30 + 0 - 0 = 30
        monthLength = 30 + extendedMonthFlag - februaryFlag;
        return monthLength;
    }
    
    /**
     * Returns string representing the output date in the format "dd-mm-yyy"
     * @return   output date string in the format "dd-mm-yyyy"
     */
    public String getOutputDateString(){
        return (outputDate[DAY] + "-" + outputDate[MONTH] + "-" + outputDate[YEAR]);
    }
    
    /**
     * Returns string representing the input date in the format "dd-mm-yyy"
     * @return   output date string in the format "dd-mm-yyyy"
     */
    public String getInputDateString(){
        return (inputDate[DAY] + "-" + inputDate[MONTH] + "-" + inputDate[YEAR]);
    }
    
    /**
     * Returns the gregorian date of the input date as a Timestamp
     * @return    the gregorian date of the input date as a Timestamp.
     */
    public Timestamp getGregorianDate(){
        return gregorianDate;
    }
    
    /**
     * Returns a formatted string for Timestamp object creation of the input date in gregorian representation.
     * @return   formatted string for Timestamp object creation
     */
    private String getDateTimestampFormat(Integer[] date){
        //Temp Variable for creating Timestamp string formatted
        //Add year string representation "yyyy"
        String tempTimestampFormat = date[YEAR].toString() + "-";
        //If month value < 10 (i.e. represented by "1" digit)
        if(date[MONTH] < 10){
            //Add preceding "0" to the month value to make its representation "2" digits "mm"
            tempTimestampFormat = tempTimestampFormat + "0";
        }
        //Add month value string
        tempTimestampFormat = tempTimestampFormat + date[MONTH] + "-";
        //If day value < 10 (i.e. represented by "1" digit)
        if(date[DAY] < 10){
            //Add preceding "0" to the day value to make its representation "2" digits "dd"
            tempTimestampFormat = tempTimestampFormat + "0";
        }
        //Add day value string
        tempTimestampFormat = tempTimestampFormat + date[DAY];
        //Add time format string with all zeros (not used).
        tempTimestampFormat = tempTimestampFormat + " 00:00:00";
        return tempTimestampFormat;
    }

-- STP_CALENDAR Database Table: Contain mapping between Hijri and Gregorian
TABLE "STP_CALENDAR"
CREATE TABLE "STP_CALENDAR" ("H_YR" VARCHAR2(4) NOT NULL ENABLE, "STRT_G_DT" DATE NOT NULL ENABLE, "END_G_DT" DATE NOT NULL ENABLE, "L1" NUMBER(2, 0) NOT NULL ENABLE, "L2" NUMBER(2, 0) NOT NULL ENABLE, "L3" NUMBER(2, 0) NOT NULL ENABLE, "L4" NUMBER(2, 0) NOT NULL ENABLE, "L5" NUMBER(2, 0) NOT NULL ENABLE, "L6" NUMBER(2, 0) NOT NULL ENABLE, "L7" NUMBER(2, 0) NOT NULL ENABLE, "L8" NUMBER(2, 0) NOT NULL ENABLE, "L9" NUMBER(2, 0) NOT NULL ENABLE, "L10" NUMBER(2, 0) NOT NULL ENABLE, "L11" NUMBER(2, 0) NOT NULL ENABLE, "L12" NUMBER(2, 0) NOT NULL ENABLE)

-


-

1 comment: