Process the hours and exceptions for an individual calendar.

@param calendar project calendar
@param calendarData hours and exception rows for this calendar
775907
private void processCalendarData(ProjectCalendar calendar, List<ResultSetRow> calendarData)
   {
      for (ResultSetRow row : calendarData)
      {
         processCalendarData(calendar, row);
      }
   }
255445
public void setCalendar(ProjectCalendar calendar)
   {
      set(TaskField.CALENDAR, calendar);
      setCalendarUniqueID(calendar == null ? null : calendar.getUniqueID());
   }
1118410
private void readCalendar(Gantt gantt)
   {
      Gantt.Calendar ganttCalendar = gantt.getCalendar();
      m_projectFile.getProjectProperties().setWeekStartDay(ganttCalendar.getWeekStart());

      ProjectCalendar calendar = m_projectFile.addCalendar();
      calendar.setName("Standard");
      m_projectFile.setDefaultCalendar(calendar);

      String workingDays = ganttCalendar.getWorkDays();
      calendar.setWorkingDay(Day.SUNDAY, workingDays.charAt(0) == '1');
      calendar.setWorkingDay(Day.MONDAY, workingDays.charAt(1) == '1');
      calendar.setWorkingDay(Day.TUESDAY, workingDays.charAt(2) == '1');
      calendar.setWorkingDay(Day.WEDNESDAY, workingDays.charAt(3) == '1');
      calendar.setWorkingDay(Day.THURSDAY, workingDays.charAt(4) == '1');
      calendar.setWorkingDay(Day.FRIDAY, workingDays.charAt(5) == '1');
      calendar.setWorkingDay(Day.SATURDAY, workingDays.charAt(6) == '1');

      for (int i = 1; i <= 7; i++)
      {
         Day day = Day.getInstance(i);
         ProjectCalendarHours hours = calendar.addCalendarHours(day);
         if (calendar.isWorkingDay(day))
         {
            hours.addRange(ProjectCalendarWeek.DEFAULT_WORKING_MORNING);
            hours.addRange(ProjectCalendarWeek.DEFAULT_WORKING_AFTERNOON);
         }
      }

      for (Gantt.Holidays.Holiday holiday : gantt.getHolidays().getHoliday())
      {
         ProjectCalendarException exception = calendar.addCalendarException(holiday.getDate(), holiday.getDate());
         exception.setName(holiday.getContent());
      }
   }
375002
private void processCalendarHours(ProjectCalendar calendar, Row row, int dayIndex)
   {
      Day day = Day.getInstance(dayIndex);
      boolean working = row.getInt("CD_WORKING") != 0;
      calendar.setWorkingDay(day, working);
      if (working == true)
      {
         ProjectCalendarHours hours = calendar.addCalendarHours(day);

         Date start = row.getDate("CD_FROM_TIME1");
         Date end = row.getDate("CD_TO_TIME1");
         if (start != null && end != null)
         {
            hours.addRange(new DateRange(start, end));
         }

         start = row.getDate("CD_FROM_TIME2");
         end = row.getDate("CD_TO_TIME2");
         if (start != null && end != null)
         {
            hours.addRange(new DateRange(start, end));
         }

         start = row.getDate("CD_FROM_TIME3");
         end = row.getDate("CD_TO_TIME3");
         if (start != null && end != null)
         {
            hours.addRange(new DateRange(start, end));
         }

         start = row.getDate("CD_FROM_TIME4");
         end = row.getDate("CD_TO_TIME4");
         if (start != null && end != null)
         {
            hours.addRange(new DateRange(start, end));
         }

         start = row.getDate("CD_FROM_TIME5");
         end = row.getDate("CD_TO_TIME5");
         if (start != null && end != null)
         {
            hours.addRange(new DateRange(start, end));
         }
      }
   }
465332
private void processCalendarData() throws MPXJException, IOException
   {
      DirectoryEntry calDir = (DirectoryEntry) m_projectDir.getEntry("TBkndCal");
      FixFix calendarFixedData = new FixFix(36, new DocumentInputStream(((DocumentEntry) calDir.getEntry("FixFix   0"))));
      FixDeferFix calendarVarData = new FixDeferFix(new DocumentInputStream(((DocumentEntry) calDir.getEntry("FixDeferFix   0"))));

      ProjectCalendar cal;
      ProjectCalendarHours hours;
      ProjectCalendarException exception;
      String name;
      byte[] baseData;
      byte[] extData;

      int periodCount;
      int index;
      int offset;
      int defaultFlag;
      Date start;
      long duration;
      int exceptionCount;

      //
      // Configure default time ranges
      //
      SimpleDateFormat df = new SimpleDateFormat("HH:mm");
      Date defaultStart1;
      Date defaultEnd1;
      Date defaultStart2;
      Date defaultEnd2;

      try
      {
         defaultStart1 = df.parse("08:00");
         defaultEnd1 = df.parse("12:00");
         defaultStart2 = df.parse("13:00");
         defaultEnd2 = df.parse("17:00");
      }

      catch (ParseException ex)
      {
         throw new MPXJException(MPXJException.INVALID_FORMAT, ex);
      }

      int calendars = calendarFixedData.getItemCount();
      int calendarID;
      int baseCalendarID;
      int periodIndex;
      Day day;
      List<Pair<ProjectCalendar, Integer>> baseCalendars = new LinkedList<Pair<ProjectCalendar, Integer>>();

      for (int loop = 0; loop < calendars; loop++)
      {
         baseData = calendarFixedData.getByteArrayValue(loop);
         calendarID = MPPUtility.getInt(baseData, 0);
         baseCalendarID = MPPUtility.getInt(baseData, 4);
         name = calendarVarData.getUnicodeString(getOffset(baseData, 20));

         //
         // Uncommenting the call to this method is useful when trying
         // to determine the function of unknown task data.
         //
         //dumpUnknownData (name + " " + MPPUtility.getInt(baseData), UNKNOWN_CALENDAR_DATA, baseData);

         //
         // Skip calendars with negative ID values
         //
         if (calendarID < 0)
         {
            continue;
         }

         //
         // Populate the basic calendar
         //
         ExtendedData ed = new ExtendedData(calendarVarData, getOffset(baseData, 32));
         offset = -1 - ed.getInt(Integer.valueOf(8));

         if (offset == -1)
         {
            if (baseCalendarID > 0)
            {
               cal = m_file.addDefaultDerivedCalendar();
               baseCalendars.add(new Pair<ProjectCalendar, Integer>(cal, Integer.valueOf(baseCalendarID)));
            }
            else
            {
               cal = m_file.addDefaultBaseCalendar();
               cal.setName(name);
            }

            cal.setUniqueID(Integer.valueOf(calendarID));
         }
         else
         {
            if (baseCalendarID > 0)
            {
               cal = m_file.addCalendar();
               baseCalendars.add(new Pair<ProjectCalendar, Integer>(cal, Integer.valueOf(baseCalendarID)));
            }
            else
            {
               cal = m_file.addCalendar();
               cal.setName(name);
            }

            cal.setUniqueID(Integer.valueOf(calendarID));

            extData = calendarVarData.getByteArray(offset);

            for (index = 0; index < 7; index++)
            {
               offset = 4 + (40 * index);

               defaultFlag = MPPUtility.getShort(extData, offset);
               day = Day.getInstance(index + 1);

               if (defaultFlag == 1)
               {
                  cal.setWorkingDay(day, DEFAULT_WORKING_WEEK[index]);
                  if (cal.isWorkingDay(day) == true)
                  {
                     hours = cal.addCalendarHours(net.sf.mpxj.Day.getInstance(index + 1));
                     hours.addRange(new DateRange(defaultStart1, defaultEnd1));
                     hours.addRange(new DateRange(defaultStart2, defaultEnd2));
                  }
               }
               else
               {
                  periodCount = MPPUtility.getShort(extData, offset + 2);
                  if (periodCount == 0)
                  {
                     cal.setWorkingDay(day, false);
                  }
                  else
                  {
                     cal.setWorkingDay(day, true);
                     hours = cal.addCalendarHours(Day.getInstance(index + 1));

                     for (periodIndex = 0; periodIndex < periodCount; periodIndex++)
                     {
                        start = MPPUtility.getTime(extData, offset + 8 + (periodIndex * 2));
                        duration = MPPUtility.getDuration(extData, offset + 16 + (periodIndex * 4));
                        hours.addRange(new DateRange(start, new Date(start.getTime() + duration)));
                     }
                  }
               }
            }

            //
            // Handle any exceptions
            //
            exceptionCount = MPPUtility.getShort(extData, 0);
            if (exceptionCount != 0)
            {
               for (index = 0; index < exceptionCount; index++)
               {
                  offset = 4 + (40 * 7) + (index * 44);

                  Date fromDate = MPPUtility.getDate(extData, offset);
                  Date toDate = MPPUtility.getDate(extData, offset + 2);
                  exception = cal.addCalendarException(fromDate, toDate);

                  periodCount = MPPUtility.getShort(extData, offset + 6);
                  if (periodCount != 0)
                  {
                     for (int exceptionPeriodIndex = 0; exceptionPeriodIndex < periodCount; exceptionPeriodIndex++)
                     {
                        start = MPPUtility.getTime(extData, offset + 12 + (exceptionPeriodIndex * 2));
                        duration = MPPUtility.getDuration(extData, offset + 20 + (exceptionPeriodIndex * 4));
                        exception.addRange(new DateRange(start, new Date(start.getTime() + duration)));
                     }
                  }
               }
            }
         }

         m_calendarMap.put(Integer.valueOf(calendarID), cal);
         m_eventManager.fireCalendarReadEvent(cal);
      }

      updateBaseCalendarNames(baseCalendars);
   }
1252015
private void processExceptionDays(ProjectCalendar mpxjCalendar, List<net.sf.mpxj.planner.schema.Day> dayList)
   {
      for (ProjectCalendarException mpxjCalendarException : mpxjCalendar.getCalendarExceptions())
      {
         Date rangeStartDay = mpxjCalendarException.getFromDate();
         Date rangeEndDay = mpxjCalendarException.getToDate();
         if (DateHelper.getDayStartDate(rangeStartDay).getTime() == DateHelper.getDayEndDate(rangeEndDay).getTime())
         {
            //
            // Exception covers a single day
            //
            net.sf.mpxj.planner.schema.Day day = m_factory.createDay();
            dayList.add(day);
            day.setType("day-type");
            day.setDate(getDateString(mpxjCalendarException.getFromDate()));
            day.setId(mpxjCalendarException.getWorking() ? "0" : "1");
         }
         else
         {
            //
            // Exception covers a range of days
            //
            Calendar cal = DateHelper.popCalendar(rangeStartDay);

            while (cal.getTime().getTime() < rangeEndDay.getTime())
            {
               net.sf.mpxj.planner.schema.Day day = m_factory.createDay();
               dayList.add(day);
               day.setType("day-type");
               day.setDate(getDateString(cal.getTime()));
               day.setId(mpxjCalendarException.getWorking() ? "0" : "1");
               cal.add(Calendar.DAY_OF_YEAR, 1);
            }
            
            DateHelper.pushCalendar(cal);
         }

         /**
          * @TODO we need to deal with date ranges here
          */
      }
   }
1043985
private void readRecurringData(ProjectCalendarException bce, Project.Calendars.Calendar.Exceptions.Exception exception)
   {
      RecurrenceType rt = getRecurrenceType(NumberHelper.getInt(exception.getType()));
      if (rt != null)
      {
         RecurringData rd = new RecurringData();
         rd.setStartDate(bce.getFromDate());
         rd.setFinishDate(bce.getToDate());
         rd.setRecurrenceType(rt);
         rd.setRelative(getRelative(NumberHelper.getInt(exception.getType())));
         rd.setOccurrences(NumberHelper.getInteger(exception.getOccurrences()));

         switch (rd.getRecurrenceType())
         {
            case DAILY:
            {
               rd.setFrequency(getFrequency(exception));
               break;
            }

            case WEEKLY:
            {
               rd.setWeeklyDaysFromBitmap(NumberHelper.getInteger(exception.getDaysOfWeek()), DAY_MASKS);
               rd.setFrequency(getFrequency(exception));
               break;
            }

            case MONTHLY:
            {
               if (rd.getRelative())
               {
                  rd.setDayOfWeek(Day.getInstance(NumberHelper.getInt(exception.getMonthItem()) - 2));
                  rd.setDayNumber(Integer.valueOf(NumberHelper.getInt(exception.getMonthPosition()) + 1));
               }
               else
               {
                  rd.setDayNumber(NumberHelper.getInteger(exception.getMonthDay()));
               }
               rd.setFrequency(getFrequency(exception));
               break;
            }

            case YEARLY:
            {
               if (rd.getRelative())
               {
                  rd.setDayOfWeek(Day.getInstance(NumberHelper.getInt(exception.getMonthItem()) - 2));
                  rd.setDayNumber(Integer.valueOf(NumberHelper.getInt(exception.getMonthPosition()) + 1));
               }
               else
               {
                  rd.setDayNumber(NumberHelper.getInteger(exception.getMonthDay()));
               }
               rd.setMonthNumber(Integer.valueOf(NumberHelper.getInt(exception.getMonth()) + 1));
               break;
            }
         }

         if (rd.getRecurrenceType() != RecurrenceType.DAILY || rd.getDates().length > 1)
         {
            bce.setRecurring(rd);
         }
      }
   }
496069
public ProjectCalendarException getException(Date date)
   {
      ProjectCalendarException exception = null;

      // We're working with expanded exceptions, which includes any recurring exceptions
      // expanded into individual entries.
      populateExpandedExceptions();
      if (!m_expandedExceptions.isEmpty())
      {
         sortExceptions();

         int low = 0;
         int high = m_expandedExceptions.size() - 1;
         long targetDate = date.getTime();

         while (low <= high)
         {
            int mid = (low + high) >>> 1;
            ProjectCalendarException midVal = m_expandedExceptions.get(mid);
            int cmp = 0 - DateHelper.compare(midVal.getFromDate(), midVal.getToDate(), targetDate);

            if (cmp < 0)
            {
               low = mid + 1;
            }
            else
            {
               if (cmp > 0)
               {
                  high = mid - 1;
               }
               else
               {
                  exception = midVal;
                  break;
               }
            }
         }
      }

      if (exception == null && getParent() != null)
      {
         // Check base calendar as well for an exception.
         exception = getParent().getException(date);
      }
      return (exception);
   }
1265083
private void writeCalendarException(ProjectCalendar parentCalendar, ProjectCalendarException record) throws IOException
   {
      m_buffer.setLength(0);
      Calendar stepDay = DateHelper.popCalendar(record.getFromDate()); // Start at From Date, then step through days...
      Calendar lastDay = DateHelper.popCalendar(record.getToDate()); // last day in this exception

      m_buffer.append("HOLI ");
      m_buffer.append(SDEFmethods.lset(parentCalendar.getUniqueID().toString(), 2));

      while (stepDay.compareTo(lastDay) <= 0)
      {
         m_buffer.append(m_formatter.format(stepDay.getTime()).toUpperCase() + " ");
         stepDay.add(Calendar.DAY_OF_MONTH, 1);
      }
      m_writer.println(m_buffer.toString());
      
      DateHelper.pushCalendar(stepDay);
      DateHelper.pushCalendar(lastDay);
   }
1269852
private void readHours(ProjectCalendar calendar, Day day, Integer hours)
   {
      int value = hours.intValue();
      int startHour = 0;
      ProjectCalendarHours calendarHours = null;

      Calendar cal = DateHelper.popCalendar();
      cal.set(Calendar.HOUR_OF_DAY, 0);
      cal.set(Calendar.MINUTE, 0);
      cal.set(Calendar.SECOND, 0);
      cal.set(Calendar.MILLISECOND, 0);

      calendar.setWorkingDay(day, false);

      while (value != 0)
      {
         // Move forward until we find a working hour
         while (startHour < 24 && (value & 0x1) == 0)
         {
            value = value >> 1;
            ++startHour;
         }

         // No more working hours, bail out
         if (startHour >= 24)
         {
            break;
         }

         // Move forward until we find the end of the working hours
         int endHour = startHour;
         while (endHour < 24 && (value & 0x1) != 0)
         {
            value = value >> 1;
            ++endHour;
         }

         cal.set(Calendar.HOUR_OF_DAY, startHour);
         Date startDate = cal.getTime();
         cal.set(Calendar.HOUR_OF_DAY, endHour);
         Date endDate = cal.getTime();

         if (calendarHours == null)
         {
            calendarHours = calendar.addCalendarHours(day);
            calendar.setWorkingDay(day, true);
         }
         calendarHours.addRange(new DateRange(startDate, endDate));
         startHour = endHour;
      }
      
      DateHelper.pushCalendar(cal);
   }
579937
private void writeCalendar(ProjectCalendar mpxj)
   {
      CalendarType xml = m_factory.createCalendarType();
      m_apibo.getCalendar().add(xml);
      String type = mpxj.getResource() == null ? "Global" : "Resource";

      xml.setBaseCalendarObjectId(getCalendarUniqueID(mpxj.getParent()));
      xml.setIsPersonal(mpxj.getResource() == null ? Boolean.FALSE : Boolean.TRUE);
      xml.setName(mpxj.getName());
      xml.setObjectId(mpxj.getUniqueID());
      xml.setType(type);

      StandardWorkWeek xmlStandardWorkWeek = m_factory.createCalendarTypeStandardWorkWeek();
      xml.setStandardWorkWeek(xmlStandardWorkWeek);

      for (Day day : EnumSet.allOf(Day.class))
      {
         StandardWorkHours xmlHours = m_factory.createCalendarTypeStandardWorkWeekStandardWorkHours();
         xmlStandardWorkWeek.getStandardWorkHours().add(xmlHours);
         xmlHours.setDayOfWeek(getDayName(day));

         for (DateRange range : mpxj.getHours(day))
         {
            WorkTimeType xmlWorkTime = m_factory.createWorkTimeType();
            xmlHours.getWorkTime().add(xmlWorkTime);

            xmlWorkTime.setStart(range.getStart());
            xmlWorkTime.setFinish(getEndTime(range.getEnd()));
         }
      }

      HolidayOrExceptions xmlExceptions = m_factory.createCalendarTypeHolidayOrExceptions();
      xml.setHolidayOrExceptions(xmlExceptions);

      if (!mpxj.getCalendarExceptions().isEmpty())
      {
         Calendar calendar = DateHelper.popCalendar();
         for (ProjectCalendarException mpxjException : mpxj.getCalendarExceptions())
         {
            calendar.setTime(mpxjException.getFromDate());
            while (calendar.getTimeInMillis() < mpxjException.getToDate().getTime())
            {
               HolidayOrException xmlException = m_factory.createCalendarTypeHolidayOrExceptionsHolidayOrException();
               xmlExceptions.getHolidayOrException().add(xmlException);

               xmlException.setDate(calendar.getTime());

               for (DateRange range : mpxjException)
               {
                  WorkTimeType xmlHours = m_factory.createWorkTimeType();
                  xmlException.getWorkTime().add(xmlHours);

                  xmlHours.setStart(range.getStart());

                  if (range.getEnd() != null)
                  {
                     xmlHours.setFinish(getEndTime(range.getEnd()));
                  }
               }
               calendar.add(Calendar.DAY_OF_YEAR, 1);
            }
         }
         DateHelper.pushCalendar(calendar);
      }
   }
1039393
public Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes createProjectCalendarsCalendarExceptionsExceptionWorkingTimes()
   {
      return new Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes();
   }
828313
private void readException(ProjectCalendar bc, Project.Calendars.Calendar.Exceptions.Exception exception)
   {
      Date fromDate = exception.getTimePeriod().getFromDate();
      Date toDate = exception.getTimePeriod().getToDate();

      // Vico Schedule Planner seems to write start and end dates to FromTime and ToTime
      // rather than FromDate and ToDate. This is plain wrong, and appears to be ignored by MS Project
      // so we will ignore it too!
      if (fromDate != null && toDate != null)
      {
         ProjectCalendarException bce = bc.addCalendarException(fromDate, toDate);
         bce.setName(exception.getName());
         readRecurringData(bce, exception);
         Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes times = exception.getWorkingTimes();
         if (times != null)
         {
            List<Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes.WorkingTime> time = times.getWorkingTime();
            for (Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes.WorkingTime period : time)
            {
               Date startTime = period.getFromTime();
               Date endTime = period.getToTime();

               if (startTime != null && endTime != null)
               {
                  if (startTime.getTime() >= endTime.getTime())
                  {
                     endTime = DateHelper.addDays(endTime, 1);
                  }

                  bce.addRange(new DateRange(startTime, endTime));
               }
            }
         }
      }
   }
430025
private void readCalendar(Project.Calendars.Calendar calendar, HashMap<BigInteger, ProjectCalendar> map, List<Pair<ProjectCalendar, BigInteger>> baseCalendars)
   {
      ProjectCalendar bc = m_projectFile.addCalendar();
      bc.setUniqueID(NumberHelper.getInteger(calendar.getUID()));
      bc.setName(calendar.getName());
      BigInteger baseCalendarID = calendar.getBaseCalendarUID();
      if (baseCalendarID != null)
      {
         baseCalendars.add(new Pair<ProjectCalendar, BigInteger>(bc, baseCalendarID));
      }

      readExceptions(calendar, bc);
      boolean readExceptionsFromDays = bc.getCalendarExceptions().isEmpty();

      Project.Calendars.Calendar.WeekDays days = calendar.getWeekDays();
      if (days != null)
      {
         for (Project.Calendars.Calendar.WeekDays.WeekDay weekDay : days.getWeekDay())
         {
            readDay(bc, weekDay, readExceptionsFromDays);
         }
      }
      else
      {
         bc.setWorkingDay(Day.SUNDAY, DayType.DEFAULT);
         bc.setWorkingDay(Day.MONDAY, DayType.DEFAULT);
         bc.setWorkingDay(Day.TUESDAY, DayType.DEFAULT);
         bc.setWorkingDay(Day.WEDNESDAY, DayType.DEFAULT);
         bc.setWorkingDay(Day.THURSDAY, DayType.DEFAULT);
         bc.setWorkingDay(Day.FRIDAY, DayType.DEFAULT);
         bc.setWorkingDay(Day.SATURDAY, DayType.DEFAULT);
      }

      readWorkWeeks(calendar, bc);

      map.put(calendar.getUID(), bc);

      m_eventManager.fireCalendarReadEvent(bc);
   }
612456
private void processSchedulingProjectProperties() throws SQLException
   {
      List<Row> rows = getRows("select * from " + m_schema + "projprop where proj_id=? and prop_name='scheduling'", m_projectID);
      if (!rows.isEmpty())
      {
         Row row = rows.get(0);
         Record record = Record.getRecord(row.getString("prop_value"));
         if (record != null)
         {
            String[] keyValues = record.getValue().split("\\|");
            for (int i = 0; i < keyValues.length - 1; ++i)
            {
               if ("sched_calendar_on_relationship_lag".equals(keyValues[i]))
               {
                  Map<String, Object> customProperties = new HashMap<String, Object>();
                  customProperties.put("LagCalendar", keyValues[i + 1]);
                  m_reader.getProject().getProjectProperties().setCustomProperties(customProperties);
                  break;
               }
            }
         }
      }
   }
109807
private void readWeekDay(ProjectCalendar mpxjCalendar, WeekDay day)
   {
      if (day.isIsDayWorking())
      {
         ProjectCalendarHours hours = mpxjCalendar.addCalendarHours(day.getDay());
         for (Document.Calendars.Calendar.WeekDays.WeekDay.TimePeriods.TimePeriod period : day.getTimePeriods().getTimePeriod())
         {
            hours.addRange(new DateRange(period.getFrom(), period.getTo()));
         }
      }
   }
379213
private void processCalendars() throws IOException
   {
      CalendarReader reader = new CalendarReader(m_data.getTableData("Calendars"));
      reader.read();

      for (MapRow row : reader.getRows())
      {
         processCalendar(row);
      }

      m_project.setDefaultCalendar(m_calendarMap.get(reader.getDefaultCalendarUUID()));
   }