This method extracts calendar data from a GanttProject file.

@param ganttProject Root node of the GanttProject file
161315
private void readCalendars(Project ganttProject)
   {
      m_mpxjCalendar = m_projectFile.addCalendar();
      m_mpxjCalendar.setName(ProjectCalendar.DEFAULT_BASE_CALENDAR_NAME);

      Calendars gpCalendar = ganttProject.getCalendars();
      setWorkingDays(m_mpxjCalendar, gpCalendar);
      setExceptions(m_mpxjCalendar, gpCalendar);
      m_eventManager.fireCalendarReadEvent(m_mpxjCalendar);
   }
1264899
public Project.Calendars.Calendar.WorkWeeks.WorkWeek.TimePeriod createProjectCalendarsCalendarWorkWeeksWorkWeekTimePeriod()
   {
      return new Project.Calendars.Calendar.WorkWeeks.WorkWeek.TimePeriod();
   }
87163
private void readCalendars()
   {
      //
      // Create the calendars
      //
      for (MapRow row : getTable("NCALTAB"))
      {
         ProjectCalendar calendar = m_projectFile.addCalendar();
         calendar.setUniqueID(row.getInteger("UNIQUE_ID"));
         calendar.setName(row.getString("NAME"));
         calendar.setWorkingDay(Day.SUNDAY, row.getBoolean("SUNDAY"));
         calendar.setWorkingDay(Day.MONDAY, row.getBoolean("MONDAY"));
         calendar.setWorkingDay(Day.TUESDAY, row.getBoolean("TUESDAY"));
         calendar.setWorkingDay(Day.WEDNESDAY, row.getBoolean("WEDNESDAY"));
         calendar.setWorkingDay(Day.THURSDAY, row.getBoolean("THURSDAY"));
         calendar.setWorkingDay(Day.FRIDAY, row.getBoolean("FRIDAY"));
         calendar.setWorkingDay(Day.SATURDAY, row.getBoolean("SATURDAY"));

         for (Day day : Day.values())
         {
            if (calendar.isWorkingDay(day))
            {
               // TODO: this is an approximation
               calendar.addDefaultCalendarHours(day);
            }
         }
      }

      //
      // Set up the hierarchy and add exceptions
      //
      Table exceptionsTable = getTable("CALXTAB");
      for (MapRow row : getTable("NCALTAB"))
      {
         ProjectCalendar child = m_projectFile.getCalendarByUniqueID(row.getInteger("UNIQUE_ID"));
         ProjectCalendar parent = m_projectFile.getCalendarByUniqueID(row.getInteger("BASE_CALENDAR_ID"));
         if (child != null && parent != null)
         {
            child.setParent(parent);
         }

         addCalendarExceptions(exceptionsTable, child, row.getInteger("FIRST_CALENDAR_EXCEPTION_ID"));

         m_eventManager.fireCalendarReadEvent(child);
      }
   }
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);
   }
664961
private void readCalendar(Calendar calendar)
   {
      ProjectCalendar mpxjCalendar = m_projectFile.addCalendar();
      mpxjCalendar.setName(calendar.getName());
      m_calendarMap.put(calendar.getID(), mpxjCalendar);

      for (WeekDay day : calendar.getWeekDays().getWeekDay())
      {
         readWeekDay(mpxjCalendar, day);
      }

      for (ExceptedDay day : calendar.getExceptedDays().getExceptedDay())
      {
         readExceptionDay(mpxjCalendar, day);
      }
   }
901296
@Override protected void processCalendarExceptions(byte[] data, ProjectCalendar cal)
   {
      //
      // Handle any exceptions
      //
      int exceptionCount = MPPUtility.getShort(data, 0);

      if (exceptionCount != 0)
      {
         int index;
         int offset;
         ProjectCalendarException exception;
         long duration;
         int periodCount;
         Date start;

         for (index = 0; index < exceptionCount; index++)
         {
            offset = 4 + (60 * 7) + (index * 64);

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

            periodCount = MPPUtility.getShort(data, offset + 6);
            if (periodCount != 0)
            {
               for (int exceptionPeriodIndex = 0; exceptionPeriodIndex < periodCount; exceptionPeriodIndex++)
               {
                  start = MPPUtility.getTime(data, offset + 12 + (exceptionPeriodIndex * 2));
                  duration = MPPUtility.getDuration(data, offset + 24 + (exceptionPeriodIndex * 4));
                  exception.addRange(new DateRange(start, new Date(start.getTime() + duration)));
               }
            }
         }
      }
   }
1060645
private void addCalendar(MpxjTreeNode parentNode, final ProjectCalendar calendar)
   {
      MpxjTreeNode calendarNode = new MpxjTreeNode(calendar, CALENDAR_EXCLUDED_METHODS)
      {
         @Override public String toString()
         {
            return calendar.getName();
         }
      };
      parentNode.add(calendarNode);

      MpxjTreeNode daysFolder = new MpxjTreeNode("Days");
      calendarNode.add(daysFolder);

      for (Day day : Day.values())
      {
         addCalendarDay(daysFolder, calendar, day);
      }

      MpxjTreeNode exceptionsFolder = new MpxjTreeNode("Exceptions");
      calendarNode.add(exceptionsFolder);

      for (ProjectCalendarException exception : calendar.getCalendarExceptions())
      {
         addCalendarException(exceptionsFolder, exception);
      }
   }
805730
private void deriveProjectCalendar()
   {
      //
      // Count the number of times each calendar is used
      //
      Map<ProjectCalendar, Integer> map = new HashMap<ProjectCalendar, Integer>();
      for (Task task : m_project.getTasks())
      {
         ProjectCalendar calendar = task.getCalendar();
         Integer count = map.get(calendar);
         if (count == null)
         {
            count = Integer.valueOf(1);
         }
         else
         {
            count = Integer.valueOf(count.intValue() + 1);
         }
         map.put(calendar, count);
      }

      //
      // Find the most frequently used calendar
      //
      int maxCount = 0;
      ProjectCalendar defaultCalendar = null;

      for (Entry<ProjectCalendar, Integer> entry : map.entrySet())
      {
         if (entry.getValue().intValue() > maxCount)
         {
            maxCount = entry.getValue().intValue();
            defaultCalendar = entry.getKey();
         }
      }

      //
      // Set the default calendar for the project
      // and remove it's use as a task-specific calendar.
      //
      if (defaultCalendar != null)
      {
         m_project.setDefaultCalendar(defaultCalendar);
         for (Task task : m_project.getTasks())
         {
            if (task.getCalendar() == defaultCalendar)
            {
               task.setCalendar(null);
            }
         }
      }
   }
1211755
private void setWorkingDays(ProjectCalendar mpxjCalendar, Calendars gpCalendar)
   {
      DayTypes dayTypes = gpCalendar.getDayTypes();
      DefaultWeek defaultWeek = dayTypes.getDefaultWeek();
      if (defaultWeek == null)
      {
         mpxjCalendar.setWorkingDay(Day.SUNDAY, false);
         mpxjCalendar.setWorkingDay(Day.MONDAY, true);
         mpxjCalendar.setWorkingDay(Day.TUESDAY, true);
         mpxjCalendar.setWorkingDay(Day.WEDNESDAY, true);
         mpxjCalendar.setWorkingDay(Day.THURSDAY, true);
         mpxjCalendar.setWorkingDay(Day.FRIDAY, true);
         mpxjCalendar.setWorkingDay(Day.SATURDAY, false);
      }
      else
      {
         mpxjCalendar.setWorkingDay(Day.MONDAY, isWorkingDay(defaultWeek.getMon()));
         mpxjCalendar.setWorkingDay(Day.TUESDAY, isWorkingDay(defaultWeek.getTue()));
         mpxjCalendar.setWorkingDay(Day.WEDNESDAY, isWorkingDay(defaultWeek.getWed()));
         mpxjCalendar.setWorkingDay(Day.THURSDAY, isWorkingDay(defaultWeek.getThu()));
         mpxjCalendar.setWorkingDay(Day.FRIDAY, isWorkingDay(defaultWeek.getFri()));
         mpxjCalendar.setWorkingDay(Day.SATURDAY, isWorkingDay(defaultWeek.getSat()));
         mpxjCalendar.setWorkingDay(Day.SUNDAY, isWorkingDay(defaultWeek.getSun()));
      }

      for (Day day : Day.values())
      {
         if (mpxjCalendar.isWorkingDay(day))
         {
            ProjectCalendarHours hours = mpxjCalendar.addCalendarHours(day);
            hours.addRange(ProjectCalendarWeek.DEFAULT_WORKING_MORNING);
            hours.addRange(ProjectCalendarWeek.DEFAULT_WORKING_AFTERNOON);
         }
      }
   }
289409
protected String extractProjectName(final BuildEvent event) {
        final Project project = event.getProject();
        return (project != null) ? project.getName() : null;
    }
478720
private void addCalendarException(MpxjTreeNode parentNode, final ProjectCalendarException exception)
   {
      MpxjTreeNode exceptionNode = new MpxjTreeNode(exception, CALENDAR_EXCEPTION_EXCLUDED_METHODS)
      {
         @Override public String toString()
         {
            return m_dateFormat.format(exception.getFromDate());
         }
      };
      parentNode.add(exceptionNode);
      addHours(exceptionNode, exception);
   }
297523
private void processWorkWeekDay(byte[] data, int offset, ProjectCalendarWeek week, Day day)
   {
      //System.out.println(ByteArrayHelper.hexdump(data, offset, 60, false));

      int dayType = MPPUtility.getShort(data, offset + 0);
      if (dayType == 1)
      {
         week.setWorkingDay(day, DayType.DEFAULT);
      }
      else
      {
         ProjectCalendarHours hours = week.addCalendarHours(day);
         int rangeCount = MPPUtility.getShort(data, offset + 2);
         if (rangeCount == 0)
         {
            week.setWorkingDay(day, DayType.NON_WORKING);
         }
         else
         {
            week.setWorkingDay(day, DayType.WORKING);
            Calendar cal = DateHelper.popCalendar();
            for (int index = 0; index < rangeCount; index++)
            {
               Date startTime = DateHelper.getCanonicalTime(MPPUtility.getTime(data, offset + 8 + (index * 2)));
               int durationInSeconds = MPPUtility.getInt(data, offset + 20 + (index * 4)) * 6;
               cal.setTime(startTime);
               cal.add(Calendar.SECOND, durationInSeconds);
               Date finishTime = DateHelper.getCanonicalTime(cal.getTime());
               hours.addRange(new DateRange(startTime, finishTime));
            }
            DateHelper.pushCalendar(cal);
         }
      }
   }
253146
@Override protected void processCalendarExceptions(byte[] data, ProjectCalendar cal)
   {
      //
      // Handle any exceptions
      //
      if (data.length > 420)
      {
         int offset = 420; // The first 420 is for the working hours data

         int exceptionCount = MPPUtility.getShort(data, offset);

         if (exceptionCount == 0)
         {
            // align with 8 byte boundary ready to read work weeks
            offset += 4;
         }
         else
         {
            ProjectCalendarException exception;
            long duration;
            int periodCount;
            Date start;

            //
            // Move to the start of the first exception
            //
            offset += 4;

            //
            // Each exception is a 92 byte block, followed by a
            // variable length text block
            //
            for (int index = 0; index < exceptionCount; index++)
            {
               if (offset + 92 > data.length)
               {
                  // Bail out if we don't have at least 92 bytes available
                  break;
               }

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

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

               //
               // Extract the name length - ensure that it is aligned to a 4 byte boundary
               //
               int exceptionNameLength = MPPUtility.getInt(data, offset + 88);
               if (exceptionNameLength % 4 != 0)
               {
                  exceptionNameLength = ((exceptionNameLength / 4) + 1) * 4;
               }

               if (exceptionNameLength != 0)
               {
                  exception.setName(MPPUtility.getUnicodeString(data, offset + 92));
               }

               //System.out.println(ByteArrayHelper.hexdump(data, offset, 92, false));

               RecurringData rd = new RecurringData();
               int recurrenceTypeValue = MPPUtility.getShort(data, offset + 72);
               rd.setStartDate(exception.getFromDate());
               rd.setFinishDate(exception.getToDate());
               rd.setRecurrenceType(getRecurrenceType(recurrenceTypeValue));
               rd.setRelative(getRelative(recurrenceTypeValue));
               rd.setOccurrences(Integer.valueOf(MPPUtility.getShort(data, offset + 4)));

               switch (rd.getRecurrenceType())
               {
                  case DAILY:
                  {
                     int frequency;
                     if (recurrenceTypeValue == 1)
                     {
                        frequency = 1;
                     }
                     else
                     {
                        frequency = MPPUtility.getShort(data, offset + 76);
                     }
                     rd.setFrequency(Integer.valueOf(frequency));
                     break;
                  }

                  case WEEKLY:
                  {
                     rd.setWeeklyDaysFromBitmap(Integer.valueOf(MPPUtility.getByte(data, offset + 76)), DAY_MASKS);
                     rd.setFrequency(Integer.valueOf(MPPUtility.getShort(data, offset + 78)));
                     break;
                  }

                  case MONTHLY:
                  {
                     if (rd.getRelative())
                     {
                        rd.setDayOfWeek(Day.getInstance(MPPUtility.getByte(data, offset + 77) - 2));
                        rd.setDayNumber(Integer.valueOf(MPPUtility.getByte(data, offset + 76) + 1));
                        rd.setFrequency(Integer.valueOf(MPPUtility.getShort(data, offset + 78)));
                     }
                     else
                     {
                        rd.setDayNumber(Integer.valueOf(MPPUtility.getByte(data, offset + 76)));
                        rd.setFrequency(Integer.valueOf(MPPUtility.getByte(data, offset + 78)));
                     }
                     break;
                  }

                  case YEARLY:
                  {
                     if (rd.getRelative())
                     {
                        rd.setDayOfWeek(Day.getInstance(MPPUtility.getByte(data, offset + 78) - 2));
                        rd.setDayNumber(Integer.valueOf(MPPUtility.getByte(data, offset + 77) + 1));
                     }
                     else
                     {
                        rd.setDayNumber(Integer.valueOf(MPPUtility.getByte(data, offset + 77)));
                     }
                     rd.setMonthNumber(Integer.valueOf(MPPUtility.getByte(data, offset + 76) + 1));
                     break;
                  }
               }

               //
               // The default values for a non-recurring exception are daily, with 1 occurrence.
               // Only add recurrence data if it is non-default.
               //
               if (rd.getRecurrenceType() != RecurrenceType.DAILY || rd.getOccurrences().intValue() != 1)
               {
                  exception.setRecurring(rd);
               }

               offset += (92 + exceptionNameLength);
            }
         }

         processWorkWeeks(data, offset, cal);
      }
   }
291935
private void writeCalendar(ProjectCalendar mpxjCalendar, net.sf.mpxj.planner.schema.Calendar plannerCalendar) throws JAXBException
   {
      //
      // Populate basic details
      //
      plannerCalendar.setId(getIntegerString(mpxjCalendar.getUniqueID()));
      plannerCalendar.setName(getString(mpxjCalendar.getName()));

      //
      // Set working and non working days
      //
      DefaultWeek dw = m_factory.createDefaultWeek();
      plannerCalendar.setDefaultWeek(dw);
      dw.setMon(getWorkingDayString(mpxjCalendar, Day.MONDAY));
      dw.setTue(getWorkingDayString(mpxjCalendar, Day.TUESDAY));
      dw.setWed(getWorkingDayString(mpxjCalendar, Day.WEDNESDAY));
      dw.setThu(getWorkingDayString(mpxjCalendar, Day.THURSDAY));
      dw.setFri(getWorkingDayString(mpxjCalendar, Day.FRIDAY));
      dw.setSat(getWorkingDayString(mpxjCalendar, Day.SATURDAY));
      dw.setSun(getWorkingDayString(mpxjCalendar, Day.SUNDAY));

      //
      // Set working hours
      //
      OverriddenDayTypes odt = m_factory.createOverriddenDayTypes();
      plannerCalendar.setOverriddenDayTypes(odt);
      List<OverriddenDayType> typeList = odt.getOverriddenDayType();
      Sequence uniqueID = new Sequence(0);

      //
      // This is a bit arbitrary, so not ideal, however...
      // The idea here is that MS Project allows us to specify working hours
      // for each day of the week individually. Planner doesn't do this,
      // but instead allows us to specify working hours for each day type.
      // What we are doing here is stepping through the days of the week to
      // find the first working day, then using the hours for that day
      // as the hours for the working day type in Planner.
      //
      for (int dayLoop = 1; dayLoop < 8; dayLoop++)
      {
         Day day = Day.getInstance(dayLoop);
         if (mpxjCalendar.isWorkingDay(day))
         {
            processWorkingHours(mpxjCalendar, uniqueID, day, typeList);
            break;
         }
      }

      //
      // Process exception days
      //
      Days plannerDays = m_factory.createDays();
      plannerCalendar.setDays(plannerDays);
      List<net.sf.mpxj.planner.schema.Day> dayList = plannerDays.getDay();
      processExceptionDays(mpxjCalendar, dayList);

      m_eventManager.fireCalendarWrittenEvent(mpxjCalendar);

      //
      // Process any derived calendars
      //
      List<net.sf.mpxj.planner.schema.Calendar> calendarList = plannerCalendar.getCalendar();

      for (ProjectCalendar mpxjDerivedCalendar : mpxjCalendar.getDerivedCalendars())
      {
         net.sf.mpxj.planner.schema.Calendar plannerDerivedCalendar = m_factory.createCalendar();
         calendarList.add(plannerDerivedCalendar);
         writeCalendar(mpxjDerivedCalendar, plannerDerivedCalendar);
      }
   }
360567
private void readCalendar(net.sf.mpxj.planner.schema.Calendar plannerCalendar, ProjectCalendar parentMpxjCalendar) throws MPXJException
   {
      //
      // Create a calendar instance
      //
      ProjectCalendar mpxjCalendar = m_projectFile.addCalendar();

      //
      // Populate basic details
      //
      mpxjCalendar.setUniqueID(getInteger(plannerCalendar.getId()));
      mpxjCalendar.setName(plannerCalendar.getName());
      mpxjCalendar.setParent(parentMpxjCalendar);

      //
      // Set working and non working days
      //
      DefaultWeek dw = plannerCalendar.getDefaultWeek();
      setWorkingDay(mpxjCalendar, Day.MONDAY, dw.getMon());
      setWorkingDay(mpxjCalendar, Day.TUESDAY, dw.getTue());
      setWorkingDay(mpxjCalendar, Day.WEDNESDAY, dw.getWed());
      setWorkingDay(mpxjCalendar, Day.THURSDAY, dw.getThu());
      setWorkingDay(mpxjCalendar, Day.FRIDAY, dw.getFri());
      setWorkingDay(mpxjCalendar, Day.SATURDAY, dw.getSat());
      setWorkingDay(mpxjCalendar, Day.SUNDAY, dw.getSun());

      //
      // Set working hours
      //
      processWorkingHours(mpxjCalendar, plannerCalendar);

      //
      // Process exception days
      //
      processExceptionDays(mpxjCalendar, plannerCalendar);

      m_eventManager.fireCalendarReadEvent(mpxjCalendar);

      //
      // Process any derived calendars
      //
      List<net.sf.mpxj.planner.schema.Calendar> calendarList = plannerCalendar.getCalendar();
      for (net.sf.mpxj.planner.schema.Calendar cal : calendarList)
      {
         readCalendar(cal, mpxjCalendar);
      }
   }
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);
   }
30546
protected void processCalendarData(ProjectCalendar calendar, Row row)
   {
      int dayIndex = row.getInt("CD_DAY_OR_EXCEPTION");
      if (dayIndex == 0)
      {
         processCalendarException(calendar, row);
      }
      else
      {
         processCalendarHours(calendar, row, dayIndex);
      }
   }