/* * file: MSPDIWriter.java * author: Jon Iles * copyright: (c) Packwood Software 2005 * date: 2005-12-30 */ /* * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; either version 2.1 of the License, or (at your * option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ package net.sf.mpxj.mspdi; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import net.sf.mpxj.AccrueType; import net.sf.mpxj.AssignmentField; import net.sf.mpxj.Availability; import net.sf.mpxj.CostRateTable; import net.sf.mpxj.CostRateTableEntry; import net.sf.mpxj.CustomField; import net.sf.mpxj.DataType; import net.sf.mpxj.DateRange; import net.sf.mpxj.Day; import net.sf.mpxj.DayType; import net.sf.mpxj.Duration; import net.sf.mpxj.EventManager; import net.sf.mpxj.FieldType; import net.sf.mpxj.ProjectCalendar; import net.sf.mpxj.ProjectCalendarException; import net.sf.mpxj.ProjectCalendarHours; import net.sf.mpxj.ProjectCalendarWeek; import net.sf.mpxj.ProjectConfig; import net.sf.mpxj.ProjectFile; import net.sf.mpxj.ProjectProperties; import net.sf.mpxj.Relation; import net.sf.mpxj.RelationType; import net.sf.mpxj.Resource; import net.sf.mpxj.ResourceAssignment; import net.sf.mpxj.ResourceField; import net.sf.mpxj.ResourceType; import net.sf.mpxj.ScheduleFrom; import net.sf.mpxj.Task; import net.sf.mpxj.TaskField; import net.sf.mpxj.TaskMode; import net.sf.mpxj.TimeUnit; import net.sf.mpxj.TimephasedWork; import net.sf.mpxj.common.AssignmentFieldLists; import net.sf.mpxj.common.DateHelper; import net.sf.mpxj.common.FieldTypeHelper; import net.sf.mpxj.common.MPPAssignmentField; import net.sf.mpxj.common.MPPResourceField; import net.sf.mpxj.common.MPPTaskField; import net.sf.mpxj.common.NumberHelper; import net.sf.mpxj.common.ResourceFieldLists; import net.sf.mpxj.common.TaskFieldLists; import net.sf.mpxj.mspdi.schema.ObjectFactory; import net.sf.mpxj.mspdi.schema.Project; import net.sf.mpxj.mspdi.schema.Project.Calendars.Calendar.Exceptions; import net.sf.mpxj.mspdi.schema.Project.Calendars.Calendar.WorkWeeks; import net.sf.mpxj.mspdi.schema.Project.Calendars.Calendar.WorkWeeks.WorkWeek; import net.sf.mpxj.mspdi.schema.Project.Calendars.Calendar.WorkWeeks.WorkWeek.TimePeriod; import net.sf.mpxj.mspdi.schema.Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays; import net.sf.mpxj.mspdi.schema.Project.Resources.Resource.AvailabilityPeriods; import net.sf.mpxj.mspdi.schema.Project.Resources.Resource.AvailabilityPeriods.AvailabilityPeriod; import net.sf.mpxj.mspdi.schema.Project.Resources.Resource.Rates; import net.sf.mpxj.mspdi.schema.TimephasedDataType; import net.sf.mpxj.writer.AbstractProjectWriter; /** * This class creates a new MSPDI file from the contents of an ProjectFile instance. */ public class MSPDIWriter extends AbstractProjectWriter { //claur removed final to extend /** * Sets a flag to control whether timephased assignment data is split * into days. The default is true. * * @param flag boolean flag */ public void setSplitTimephasedAsDays(boolean flag) { m_splitTimephasedAsDays = flag; } /** * Retrieves a flag to control whether timephased assignment data is split * into days. The default is true. * * @return boolean true */ public boolean getSplitTimephasedAsDays() { return m_splitTimephasedAsDays; } /** * Sets a flag to control whether timephased resource assignment data * is written to the file. The default is false. * * @param value boolean flag */ public void setWriteTimephasedData(boolean value) { m_writeTimphasedData = value; } /** * Retrieves the state of the flag which controls whether timephased * resource assignment data is written to the file. The default is false. * * @return boolean flag */ public boolean getWriteTimephasedData() { return m_writeTimphasedData; } /** * Set the save version to use when generating an MSPDI file. * * @param version save version */ public void setSaveVersion(SaveVersion version) { m_saveVersion = version; } /** * Retrieve the save version current set. * * @return current save version */ public SaveVersion getSaveVersion() { return m_saveVersion; } /** * {@inheritDoc} */ @Override public void write(ProjectFile projectFile, OutputStream stream) throws IOException { try { if (CONTEXT == null) { throw CONTEXT_EXCEPTION; } m_projectFile = projectFile; m_projectFile.validateUniqueIDsForMicrosoftProject(); m_eventManager = m_projectFile.getEventManager(); Marshaller marshaller = CONTEXT.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); m_extendedAttributesInUse = new HashSet<FieldType>(); m_factory = new ObjectFactory(); Project project = m_factory.createProject(); writeProjectProperties(project); writeCalendars(project); writeResources(project); writeTasks(project); writeAssignments(project); writeProjectExtendedAttributes(project); DatatypeConverter.setParentFile(m_projectFile); marshaller.marshal(project, stream); } catch (JAXBException ex) { throw new IOException(ex.toString()); } finally { m_projectFile = null; m_factory = null; m_extendedAttributesInUse = null; } } /** * This method writes project properties to an MSPDI file. * * @param project Root node of the MSPDI file */ private void writeProjectProperties(Project project) { ProjectProperties properties = m_projectFile.getProjectProperties(); project.setActualsInSync(Boolean.valueOf(properties.getActualsInSync())); project.setAdminProject(Boolean.valueOf(properties.getAdminProject())); project.setAuthor(properties.getAuthor()); project.setAutoAddNewResourcesAndTasks(Boolean.valueOf(properties.getAutoAddNewResourcesAndTasks())); project.setAutolink(Boolean.valueOf(properties.getAutolink())); project.setBaselineForEarnedValue(NumberHelper.getBigInteger(properties.getBaselineForEarnedValue())); project.setCalendarUID(m_projectFile.getDefaultCalendar() == null ? BigInteger.ONE : NumberHelper.getBigInteger(m_projectFile.getDefaultCalendar().getUniqueID())); project.setCategory(properties.getCategory()); project.setCompany(properties.getCompany()); project.setCreationDate(DatatypeConverter.printDate(properties.getCreationDate())); project.setCriticalSlackLimit(NumberHelper.getBigInteger(properties.getCriticalSlackLimit())); project.setCurrencyCode(properties.getCurrencyCode()); project.setCurrencyDigits(BigInteger.valueOf(properties.getCurrencyDigits().intValue())); project.setCurrencySymbol(properties.getCurrencySymbol()); project.setCurrencySymbolPosition(properties.getSymbolPosition()); project.setCurrentDate(DatatypeConverter.printDate(properties.getCurrentDate())); project.setDaysPerMonth(NumberHelper.getBigInteger(properties.getDaysPerMonth())); project.setDefaultFinishTime(DatatypeConverter.printTime(properties.getDefaultEndTime())); project.setDefaultFixedCostAccrual(properties.getDefaultFixedCostAccrual()); project.setDefaultOvertimeRate(DatatypeConverter.printRate(properties.getDefaultOvertimeRate())); project.setDefaultStandardRate(DatatypeConverter.printRate(properties.getDefaultStandardRate())); project.setDefaultStartTime(DatatypeConverter.printTime(properties.getDefaultStartTime())); project.setDefaultTaskEVMethod(DatatypeConverter.printEarnedValueMethod(properties.getDefaultTaskEarnedValueMethod())); project.setDefaultTaskType(properties.getDefaultTaskType()); project.setDurationFormat(DatatypeConverter.printDurationTimeUnits(properties.getDefaultDurationUnits(), false)); project.setEarnedValueMethod(DatatypeConverter.printEarnedValueMethod(properties.getEarnedValueMethod())); project.setEditableActualCosts(Boolean.valueOf(properties.getEditableActualCosts())); project.setExtendedCreationDate(DatatypeConverter.printDate(properties.getExtendedCreationDate())); project.setFinishDate(DatatypeConverter.printDate(properties.getFinishDate())); project.setFiscalYearStart(Boolean.valueOf(properties.getFiscalYearStart())); project.setFYStartDate(NumberHelper.getBigInteger(properties.getFiscalYearStartMonth())); project.setHonorConstraints(Boolean.valueOf(properties.getHonorConstraints())); project.setInsertedProjectsLikeSummary(Boolean.valueOf(properties.getInsertedProjectsLikeSummary())); project.setLastSaved(DatatypeConverter.printDate(properties.getLastSaved())); project.setManager(properties.getManager()); project.setMicrosoftProjectServerURL(Boolean.valueOf(properties.getMicrosoftProjectServerURL())); project.setMinutesPerDay(NumberHelper.getBigInteger(properties.getMinutesPerDay())); project.setMinutesPerWeek(NumberHelper.getBigInteger(properties.getMinutesPerWeek())); project.setMoveCompletedEndsBack(Boolean.valueOf(properties.getMoveCompletedEndsBack())); project.setMoveCompletedEndsForward(Boolean.valueOf(properties.getMoveCompletedEndsForward())); project.setMoveRemainingStartsBack(Boolean.valueOf(properties.getMoveRemainingStartsBack())); project.setMoveRemainingStartsForward(Boolean.valueOf(properties.getMoveRemainingStartsForward())); project.setMultipleCriticalPaths(Boolean.valueOf(properties.getMultipleCriticalPaths())); project.setName(properties.getName()); project.setNewTasksEffortDriven(Boolean.valueOf(properties.getNewTasksEffortDriven())); project.setNewTasksEstimated(Boolean.valueOf(properties.getNewTasksEstimated())); project.setNewTaskStartDate(properties.getNewTaskStartIsProjectStart() == true ? BigInteger.ZERO : BigInteger.ONE); project.setProjectExternallyEdited(Boolean.valueOf(properties.getProjectExternallyEdited())); project.setRemoveFileProperties(Boolean.valueOf(properties.getRemoveFileProperties())); project.setRevision(NumberHelper.getBigInteger(properties.getRevision())); project.setSaveVersion(BigInteger.valueOf(m_saveVersion.getValue())); project.setScheduleFromStart(Boolean.valueOf(properties.getScheduleFrom() == ScheduleFrom.START)); project.setSplitsInProgressTasks(Boolean.valueOf(properties.getSplitInProgressTasks())); project.setSpreadActualCost(Boolean.valueOf(properties.getSpreadActualCost())); project.setSpreadPercentComplete(Boolean.valueOf(properties.getSpreadPercentComplete())); project.setStartDate(DatatypeConverter.printDate(properties.getStartDate())); project.setStatusDate(DatatypeConverter.printDate(properties.getStatusDate())); project.setSubject(properties.getSubject()); project.setTaskUpdatesResource(Boolean.valueOf(properties.getUpdatingTaskStatusUpdatesResourceStatus())); project.setTitle(properties.getProjectTitle()); project.setUID(properties.getUniqueID()); project.setWeekStartDay(DatatypeConverter.printDay(properties.getWeekStartDay())); project.setWorkFormat(DatatypeConverter.printWorkUnits(properties.getDefaultWorkUnits())); } /** * This method writes project extended attribute data into an MSPDI file. * * @param project Root node of the MSPDI file */ private void writeProjectExtendedAttributes(Project project) { Project.ExtendedAttributes attributes = m_factory.createProjectExtendedAttributes(); project.setExtendedAttributes(attributes); List<Project.ExtendedAttributes.ExtendedAttribute> list = attributes.getExtendedAttribute(); for (CustomField field : m_projectFile.getCustomFields()) { FieldType fieldType = field.getFieldType(); String alias = field.getAlias(); if (m_extendedAttributesInUse.contains(fieldType) || alias != null) { Project.ExtendedAttributes.ExtendedAttribute attribute = m_factory.createProjectExtendedAttributesExtendedAttribute(); list.add(attribute); attribute.setFieldID(String.valueOf(FieldTypeHelper.getFieldID(fieldType))); attribute.setFieldName(fieldType.getName()); attribute.setAlias(alias); } } } /** * This method writes calendar data to an MSPDI file. * * @param project Root node of the MSPDI file */ private void writeCalendars(Project project) { // // Create the new MSPDI calendar list // Project.Calendars calendars = m_factory.createProjectCalendars(); project.setCalendars(calendars); List<Project.Calendars.Calendar> calendar = calendars.getCalendar(); // // Process each calendar in turn // for (ProjectCalendar cal : m_projectFile.getCalendars()) { calendar.add(writeCalendar(cal)); } } /** * This method writes data for a single calendar to an MSPDI file. * * @param bc Base calendar data * @return New MSPDI calendar instance */ private Project.Calendars.Calendar writeCalendar(ProjectCalendar bc) { // // Create a calendar // Project.Calendars.Calendar calendar = m_factory.createProjectCalendarsCalendar(); calendar.setUID(NumberHelper.getBigInteger(bc.getUniqueID())); calendar.setIsBaseCalendar(Boolean.valueOf(!bc.isDerived())); ProjectCalendar base = bc.getParent(); if (base != null) { calendar.setBaseCalendarUID(NumberHelper.getBigInteger(base.getUniqueID())); } calendar.setName(bc.getName()); // // Create a list of normal days // Project.Calendars.Calendar.WeekDays days = m_factory.createProjectCalendarsCalendarWeekDays(); Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime time; ProjectCalendarHours bch; List<Project.Calendars.Calendar.WeekDays.WeekDay> dayList = days.getWeekDay(); for (int loop = 1; loop < 8; loop++) { DayType workingFlag = bc.getWorkingDay(Day.getInstance(loop)); if (workingFlag != DayType.DEFAULT) { Project.Calendars.Calendar.WeekDays.WeekDay day = m_factory.createProjectCalendarsCalendarWeekDaysWeekDay(); dayList.add(day); day.setDayType(BigInteger.valueOf(loop)); day.setDayWorking(Boolean.valueOf(workingFlag == DayType.WORKING)); if (workingFlag == DayType.WORKING) { Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes times = m_factory.createProjectCalendarsCalendarWeekDaysWeekDayWorkingTimes(); day.setWorkingTimes(times); List<Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime> timesList = times.getWorkingTime(); bch = bc.getCalendarHours(Day.getInstance(loop)); if (bch != null) { for (DateRange range : bch) { if (range != null) { time = m_factory.createProjectCalendarsCalendarWeekDaysWeekDayWorkingTimesWorkingTime(); timesList.add(time); time.setFromTime(DatatypeConverter.printTime(range.getStart())); time.setToTime(DatatypeConverter.printTime(range.getEnd())); } } } } } } // // Create a list of exceptions // // A quirk of MS Project is that these exceptions must be // in date order in the file, otherwise they are ignored // List<ProjectCalendarException> exceptions = new ArrayList<ProjectCalendarException>(bc.getCalendarExceptions()); if (!exceptions.isEmpty()) { Collections.sort(exceptions); writeExceptions(calendar, dayList, exceptions); } // // Do not add a weekdays tag to the calendar unless it // has valid entries. // Fixes SourceForge bug 1854747: MPXJ and MSP 2007 XML formats // if (!dayList.isEmpty()) { calendar.setWeekDays(days); } writeWorkWeeks(calendar, bc); m_eventManager.fireCalendarWrittenEvent(bc); return (calendar); } /** * Main entry point used to determine the format used to write * calendar exceptions. * * @param calendar parent calendar * @param dayList list of calendar days * @param exceptions list of exceptions */ private void writeExceptions(Project.Calendars.Calendar calendar, List<Project.Calendars.Calendar.WeekDays.WeekDay> dayList, List<ProjectCalendarException> exceptions) { if (m_saveVersion.getValue() < SaveVersion.Project2007.getValue()) { writeExcepions9(dayList, exceptions); } else { writeExcepions12(calendar, exceptions); } } /** * Write exceptions in the format used by MSPDI files prior to Project 2007. * * @param dayList list of calendar days * @param exceptions list of exceptions */ private void writeExcepions9(List<Project.Calendars.Calendar.WeekDays.WeekDay> dayList, List<ProjectCalendarException> exceptions) { for (ProjectCalendarException exception : exceptions) { boolean working = exception.getWorking(); Project.Calendars.Calendar.WeekDays.WeekDay day = m_factory.createProjectCalendarsCalendarWeekDaysWeekDay(); dayList.add(day); day.setDayType(BIGINTEGER_ZERO); day.setDayWorking(Boolean.valueOf(working)); Project.Calendars.Calendar.WeekDays.WeekDay.TimePeriod period = m_factory.createProjectCalendarsCalendarWeekDaysWeekDayTimePeriod(); day.setTimePeriod(period); period.setFromDate(DatatypeConverter.printDate(exception.getFromDate())); period.setToDate(DatatypeConverter.printDate(exception.getToDate())); if (working) { Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes times = m_factory.createProjectCalendarsCalendarWeekDaysWeekDayWorkingTimes(); day.setWorkingTimes(times); List<Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime> timesList = times.getWorkingTime(); for (DateRange range : exception) { Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime time = m_factory.createProjectCalendarsCalendarWeekDaysWeekDayWorkingTimesWorkingTime(); timesList.add(time); time.setFromTime(DatatypeConverter.printTime(range.getStart())); time.setToTime(DatatypeConverter.printTime(range.getEnd())); } } } } /** * Write exceptions into the format used by MSPDI files from * Project 2007 onwards. * * @param calendar parent calendar * @param exceptions list of exceptions */ private void writeExcepions12(Project.Calendars.Calendar calendar, List<ProjectCalendarException> exceptions) { Exceptions ce = m_factory.createProjectCalendarsCalendarExceptions(); calendar.setExceptions(ce); List<Exceptions.Exception> el = ce.getException(); for (ProjectCalendarException exception : exceptions) { Exceptions.Exception ex = m_factory.createProjectCalendarsCalendarExceptionsException(); el.add(ex); ex.setEnteredByOccurrences(Boolean.FALSE); ex.setOccurrences(BigInteger.ONE); ex.setType(BigInteger.ONE); boolean working = exception.getWorking(); ex.setDayWorking(Boolean.valueOf(working)); Project.Calendars.Calendar.Exceptions.Exception.TimePeriod period = m_factory.createProjectCalendarsCalendarExceptionsExceptionTimePeriod(); ex.setTimePeriod(period); period.setFromDate(DatatypeConverter.printDate(exception.getFromDate())); period.setToDate(DatatypeConverter.printDate(exception.getToDate())); if (working) { Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes times = m_factory.createProjectCalendarsCalendarExceptionsExceptionWorkingTimes(); ex.setWorkingTimes(times); List<Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes.WorkingTime> timesList = times.getWorkingTime(); for (DateRange range : exception) { Project.Calendars.Calendar.Exceptions.Exception.WorkingTimes.WorkingTime time = m_factory.createProjectCalendarsCalendarExceptionsExceptionWorkingTimesWorkingTime(); timesList.add(time); time.setFromTime(DatatypeConverter.printTime(range.getStart())); time.setToTime(DatatypeConverter.printTime(range.getEnd())); } } } } /** * Write the work weeks associated with this calendar. * * @param xmlCalendar XML calendar instance * @param mpxjCalendar MPXJ calendar instance */ private void writeWorkWeeks(Project.Calendars.Calendar xmlCalendar, ProjectCalendar mpxjCalendar) { List<ProjectCalendarWeek> weeks = mpxjCalendar.getWorkWeeks(); if (!weeks.isEmpty()) { WorkWeeks xmlWorkWeeks = m_factory.createProjectCalendarsCalendarWorkWeeks(); xmlCalendar.setWorkWeeks(xmlWorkWeeks); List<WorkWeek> xmlWorkWeekList = xmlWorkWeeks.getWorkWeek(); for (ProjectCalendarWeek week : weeks) { WorkWeek xmlWeek = m_factory.createProjectCalendarsCalendarWorkWeeksWorkWeek(); xmlWorkWeekList.add(xmlWeek); xmlWeek.setName(week.getName()); TimePeriod xmlTimePeriod = m_factory.createProjectCalendarsCalendarWorkWeeksWorkWeekTimePeriod(); xmlWeek.setTimePeriod(xmlTimePeriod); xmlTimePeriod.setFromDate(DatatypeConverter.printDate(week.getDateRange().getStart())); xmlTimePeriod.setToDate(DatatypeConverter.printDate(week.getDateRange().getEnd())); WeekDays xmlWeekDays = m_factory.createProjectCalendarsCalendarWorkWeeksWorkWeekWeekDays(); xmlWeek.setWeekDays(xmlWeekDays); List<Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay> dayList = xmlWeekDays.getWeekDay(); for (int loop = 1; loop < 8; loop++) { DayType workingFlag = week.getWorkingDay(Day.getInstance(loop)); if (workingFlag != DayType.DEFAULT) { Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay day = m_factory.createProjectCalendarsCalendarWorkWeeksWorkWeekWeekDaysWeekDay(); dayList.add(day); day.setDayType(BigInteger.valueOf(loop)); day.setDayWorking(Boolean.valueOf(workingFlag == DayType.WORKING)); if (workingFlag == DayType.WORKING) { Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay.WorkingTimes times = m_factory.createProjectCalendarsCalendarWorkWeeksWorkWeekWeekDaysWeekDayWorkingTimes(); day.setWorkingTimes(times); List<Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay.WorkingTimes.WorkingTime> timesList = times.getWorkingTime(); ProjectCalendarHours bch = week.getCalendarHours(Day.getInstance(loop)); if (bch != null) { for (DateRange range : bch) { if (range != null) { Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay.WorkingTimes.WorkingTime time = m_factory.createProjectCalendarsCalendarWorkWeeksWorkWeekWeekDaysWeekDayWorkingTimesWorkingTime(); timesList.add(time); time.setFromTime(DatatypeConverter.printTime(range.getStart())); time.setToTime(DatatypeConverter.printTime(range.getEnd())); } } } } } } } } } /** * This method writes resource data to an MSPDI file. * * @param project Root node of the MSPDI file */ protected void writeResources(Project project) //claur changed to protected { Project.Resources resources = m_factory.createProjectResources(); project.setResources(resources); List<Project.Resources.Resource> list = resources.getResource(); for (Resource resource : m_projectFile.getAllResources()) { list.add(writeResource(resource)); } } /** * This method writes data for a single resource to an MSPDI file. * * @param mpx Resource data * @return New MSPDI resource instance */ protected Project.Resources.Resource writeResource(Resource mpx) //claur changed to protected { Project.Resources.Resource xml = m_factory.createProjectResourcesResource(); ProjectCalendar cal = mpx.getResourceCalendar(); if (cal != null) { xml.setCalendarUID(NumberHelper.getBigInteger(cal.getUniqueID())); } xml.setAccrueAt(mpx.getAccrueAt()); xml.setActiveDirectoryGUID(mpx.getActiveDirectoryGUID()); xml.setActualCost(DatatypeConverter.printCurrency(mpx.getActualCost())); xml.setActualOvertimeCost(DatatypeConverter.printCurrency(mpx.getActualOvertimeCost())); xml.setActualOvertimeWork(DatatypeConverter.printDuration(this, mpx.getActualOvertimeWork())); xml.setActualOvertimeWorkProtected(DatatypeConverter.printDuration(this, mpx.getActualOvertimeWorkProtected())); xml.setActualWork(DatatypeConverter.printDuration(this, mpx.getActualWork())); xml.setActualWorkProtected(DatatypeConverter.printDuration(this, mpx.getActualWorkProtected())); xml.setACWP(DatatypeConverter.printCurrency(mpx.getACWP())); xml.setAvailableFrom(DatatypeConverter.printDate(mpx.getAvailableFrom())); xml.setAvailableTo(DatatypeConverter.printDate(mpx.getAvailableTo())); xml.setBCWS(DatatypeConverter.printCurrency(mpx.getBCWS())); xml.setBCWP(DatatypeConverter.printCurrency(mpx.getBCWP())); xml.setBookingType(mpx.getBookingType()); xml.setIsBudget(Boolean.valueOf(mpx.getBudget())); xml.setCanLevel(Boolean.valueOf(mpx.getCanLevel())); xml.setCode(mpx.getCode()); xml.setCost(DatatypeConverter.printCurrency(mpx.getCost())); xml.setCostPerUse(DatatypeConverter.printCurrency(mpx.getCostPerUse())); xml.setCostVariance(DatatypeConverter.printCurrency(mpx.getCostVariance())); xml.setCreationDate(DatatypeConverter.printDate(mpx.getCreationDate())); xml.setCV(DatatypeConverter.printCurrency(mpx.getCV())); xml.setEmailAddress(mpx.getEmailAddress()); xml.setFinish(DatatypeConverter.printDate(mpx.getFinish())); xml.setGroup(mpx.getGroup()); xml.setHyperlink(mpx.getHyperlink()); xml.setHyperlinkAddress(mpx.getHyperlinkAddress()); xml.setHyperlinkSubAddress(mpx.getHyperlinkSubAddress()); xml.setID(NumberHelper.getBigInteger(mpx.getID())); xml.setInitials(mpx.getInitials()); xml.setIsEnterprise(Boolean.valueOf(mpx.getEnterprise())); xml.setIsGeneric(Boolean.valueOf(mpx.getGeneric())); xml.setIsInactive(Boolean.valueOf(mpx.getInactive())); xml.setIsNull(Boolean.valueOf(mpx.getNull())); xml.setMaterialLabel(mpx.getMaterialLabel()); xml.setMaxUnits(DatatypeConverter.printUnits(mpx.getMaxUnits())); xml.setName(mpx.getName()); if (!mpx.getNotes().isEmpty()) { xml.setNotes(mpx.getNotes()); } xml.setNTAccount(mpx.getNtAccount()); xml.setOverAllocated(Boolean.valueOf(mpx.getOverAllocated())); xml.setOvertimeCost(DatatypeConverter.printCurrency(mpx.getOvertimeCost())); xml.setOvertimeRate(DatatypeConverter.printRate(mpx.getOvertimeRate())); xml.setOvertimeRateFormat(DatatypeConverter.printTimeUnit(mpx.getOvertimeRateUnits())); xml.setOvertimeWork(DatatypeConverter.printDuration(this, mpx.getOvertimeWork())); xml.setPeakUnits(DatatypeConverter.printUnits(mpx.getPeakUnits())); xml.setPercentWorkComplete(NumberHelper.getBigInteger(mpx.getPercentWorkComplete())); xml.setPhonetics(mpx.getPhonetics()); xml.setRegularWork(DatatypeConverter.printDuration(this, mpx.getRegularWork())); xml.setRemainingCost(DatatypeConverter.printCurrency(mpx.getRemainingCost())); xml.setRemainingOvertimeCost(DatatypeConverter.printCurrency(mpx.getRemainingOvertimeCost())); xml.setRemainingOvertimeWork(DatatypeConverter.printDuration(this, mpx.getRemainingOvertimeWork())); xml.setRemainingWork(DatatypeConverter.printDuration(this, mpx.getRemainingWork())); xml.setStandardRate(DatatypeConverter.printRate(mpx.getStandardRate())); xml.setStandardRateFormat(DatatypeConverter.printTimeUnit(mpx.getStandardRateUnits())); xml.setStart(DatatypeConverter.printDate(mpx.getStart())); xml.setSV(DatatypeConverter.printCurrency(mpx.getSV())); xml.setUID(mpx.getUniqueID()); xml.setWork(DatatypeConverter.printDuration(this, mpx.getWork())); xml.setWorkGroup(mpx.getWorkGroup()); xml.setWorkVariance(DatatypeConverter.printDurationInDecimalThousandthsOfMinutes(mpx.getWorkVariance())); if (mpx.getType() == ResourceType.COST) { xml.setType(ResourceType.MATERIAL); xml.setIsCostResource(Boolean.TRUE); } else { xml.setType(mpx.getType()); } writeResourceExtendedAttributes(xml, mpx); writeResourceBaselines(xml, mpx); writeCostRateTables(xml, mpx); writeAvailability(xml, mpx); return (xml); } /** * Writes resource baseline data. * * @param xmlResource MSPDI resource * @param mpxjResource MPXJ resource */ private void writeResourceBaselines(Project.Resources.Resource xmlResource, Resource mpxjResource) { Project.Resources.Resource.Baseline baseline = m_factory.createProjectResourcesResourceBaseline(); boolean populated = false; Number cost = mpxjResource.getBaselineCost(); if (cost != null && cost.intValue() != 0) { populated = true; baseline.setCost(DatatypeConverter.printCurrency(cost)); } Duration work = mpxjResource.getBaselineWork(); if (work != null && work.getDuration() != 0) { populated = true; baseline.setWork(DatatypeConverter.printDuration(this, work)); } if (populated) { xmlResource.getBaseline().add(baseline); baseline.setNumber(BigInteger.ZERO); } for (int loop = 1; loop <= 10; loop++) { baseline = m_factory.createProjectResourcesResourceBaseline(); populated = false; cost = mpxjResource.getBaselineCost(loop); if (cost != null && cost.intValue() != 0) { populated = true; baseline.setCost(DatatypeConverter.printCurrency(cost)); } work = mpxjResource.getBaselineWork(loop); if (work != null && work.getDuration() != 0) { populated = true; baseline.setWork(DatatypeConverter.printDuration(this, work)); } if (populated) { xmlResource.getBaseline().add(baseline); baseline.setNumber(BigInteger.valueOf(loop)); } } } /** * This method writes extended attribute data for a resource. * * @param xml MSPDI resource * @param mpx MPXJ resource */ private void writeResourceExtendedAttributes(Project.Resources.Resource xml, Resource mpx) { Project.Resources.Resource.ExtendedAttribute attrib; List<Project.Resources.Resource.ExtendedAttribute> extendedAttributes = xml.getExtendedAttribute(); for (ResourceField mpxFieldID : getAllResourceExtendedAttributes()) { Object value = mpx.getCachedValue(mpxFieldID); if (writeExtendedAttribute(value, mpxFieldID)) { m_extendedAttributesInUse.add(mpxFieldID); Integer xmlFieldID = Integer.valueOf(MPPResourceField.getID(mpxFieldID) | MPPResourceField.RESOURCE_FIELD_BASE); attrib = m_factory.createProjectResourcesResourceExtendedAttribute(); extendedAttributes.add(attrib); attrib.setFieldID(xmlFieldID.toString()); attrib.setValue(DatatypeConverter.printExtendedAttribute(this, value, mpxFieldID.getDataType())); attrib.setDurationFormat(printExtendedAttributeDurationFormat(value)); } } } /** * This method is called to determine if an extended attribute * should be written to the file, or whether the default value * can be assumed. * * @param value extended attribute value * @param type extended attribute data type * @return boolean flag */ private boolean writeExtendedAttribute(Object value, FieldType type) { boolean write = true; if (value == null) { write = false; } else { DataType dataType = type.getDataType(); switch (dataType) { case BOOLEAN: { write = ((Boolean) value).booleanValue(); break; } case CURRENCY: case NUMERIC: { write = !NumberHelper.equals(((Number) value).doubleValue(), 0.0, 0.00001); break; } case DURATION: { write = (((Duration) value).getDuration() != 0); break; } default: { break; } } } return write; } /** * Writes a resource's cost rate tables. * * @param xml MSPDI resource * @param mpx MPXJ resource */ private void writeCostRateTables(Project.Resources.Resource xml, Resource mpx) { //Rates rates = m_factory.createProjectResourcesResourceRates(); //xml.setRates(rates); //List<Project.Resources.Resource.Rates.Rate> ratesList = rates.getRate(); List<Project.Resources.Resource.Rates.Rate> ratesList = null; for (int tableIndex = 0; tableIndex < 5; tableIndex++) { CostRateTable table = mpx.getCostRateTable(tableIndex); if (table != null) { Date from = DateHelper.FIRST_DATE; for (CostRateTableEntry entry : table) { if (costRateTableWriteRequired(entry, from)) { if (ratesList == null) { Rates rates = m_factory.createProjectResourcesResourceRates(); xml.setRates(rates); ratesList = rates.getRate(); } Project.Resources.Resource.Rates.Rate rate = m_factory.createProjectResourcesResourceRatesRate(); ratesList.add(rate); rate.setCostPerUse(DatatypeConverter.printCurrency(entry.getCostPerUse())); rate.setOvertimeRate(DatatypeConverter.printRate(entry.getOvertimeRate())); rate.setOvertimeRateFormat(DatatypeConverter.printTimeUnit(entry.getOvertimeRateFormat())); rate.setRatesFrom(DatatypeConverter.printDate(from)); from = entry.getEndDate(); rate.setRatesTo(DatatypeConverter.printDate(from)); rate.setRateTable(BigInteger.valueOf(tableIndex)); rate.setStandardRate(DatatypeConverter.printRate(entry.getStandardRate())); rate.setStandardRateFormat(DatatypeConverter.printTimeUnit(entry.getStandardRateFormat())); } } } } } /** * This method determines whether the cost rate table should be written. * A default cost rate table should not be written to the file. * * @param entry cost rate table entry * @param from from date * @return boolean flag */ private boolean costRateTableWriteRequired(CostRateTableEntry entry, Date from) { boolean fromDate = (DateHelper.compare(from, DateHelper.FIRST_DATE) > 0); boolean toDate = (DateHelper.compare(entry.getEndDate(), DateHelper.LAST_DATE) > 0); boolean costPerUse = (NumberHelper.getDouble(entry.getCostPerUse()) != 0); boolean overtimeRate = (entry.getOvertimeRate() != null && entry.getOvertimeRate().getAmount() != 0); boolean standardRate = (entry.getStandardRate() != null && entry.getStandardRate().getAmount() != 0); return (fromDate || toDate || costPerUse || overtimeRate || standardRate); } /** * This method writes a resource's availability table. * * @param xml MSPDI resource * @param mpx MPXJ resource */ private void writeAvailability(Project.Resources.Resource xml, Resource mpx) { AvailabilityPeriods periods = m_factory.createProjectResourcesResourceAvailabilityPeriods(); xml.setAvailabilityPeriods(periods); List<AvailabilityPeriod> list = periods.getAvailabilityPeriod(); for (Availability availability : mpx.getAvailability()) { AvailabilityPeriod period = m_factory.createProjectResourcesResourceAvailabilityPeriodsAvailabilityPeriod(); list.add(period); DateRange range = availability.getRange(); period.setAvailableFrom(DatatypeConverter.printDate(range.getStart())); period.setAvailableTo(DatatypeConverter.printDate(range.getEnd())); period.setAvailableUnits(DatatypeConverter.printUnits(availability.getUnits())); } } /** * This method writes task data to an MSPDI file. * * @param project Root node of the MSPDI file */ private void writeTasks(Project project) { Project.Tasks tasks = m_factory.createProjectTasks(); project.setTasks(tasks); List<Project.Tasks.Task> list = tasks.getTask(); for (Task task : m_projectFile.getAllTasks()) { list.add(writeTask(task)); } } /** * This method writes data for a single task to an MSPDI file. * * @param mpx Task data * @return new task instance */ protected Project.Tasks.Task writeTask(Task mpx) //claur changed to protected { Project.Tasks.Task xml = m_factory.createProjectTasksTask(); xml.setActive(Boolean.valueOf(mpx.getActive())); xml.setActualCost(DatatypeConverter.printCurrency(mpx.getActualCost())); xml.setActualDuration(DatatypeConverter.printDuration(this, mpx.getActualDuration())); xml.setActualFinish(DatatypeConverter.printDate(mpx.getActualFinish())); xml.setActualOvertimeCost(DatatypeConverter.printCurrency(mpx.getActualOvertimeCost())); xml.setActualOvertimeWork(DatatypeConverter.printDuration(this, mpx.getActualOvertimeWork())); xml.setActualOvertimeWorkProtected(DatatypeConverter.printDuration(this, mpx.getActualOvertimeWorkProtected())); xml.setActualStart(DatatypeConverter.printDate(mpx.getActualStart())); xml.setActualWork(DatatypeConverter.printDuration(this, mpx.getActualWork())); xml.setActualWorkProtected(DatatypeConverter.printDuration(this, mpx.getActualWorkProtected())); xml.setACWP(DatatypeConverter.printCurrency(mpx.getACWP())); xml.setBCWP(DatatypeConverter.printCurrency(mpx.getBCWP())); xml.setBCWS(DatatypeConverter.printCurrency(mpx.getBCWS())); xml.setCalendarUID(getTaskCalendarID(mpx)); xml.setConstraintDate(DatatypeConverter.printDate(mpx.getConstraintDate())); xml.setConstraintType(DatatypeConverter.printConstraintType(mpx.getConstraintType())); xml.setContact(mpx.getContact()); xml.setCost(DatatypeConverter.printCurrency(mpx.getCost())); xml.setCreateDate(DatatypeConverter.printDate(mpx.getCreateDate())); xml.setCritical(Boolean.valueOf(mpx.getCritical())); xml.setCV(DatatypeConverter.printCurrency(mpx.getCV())); xml.setDeadline(DatatypeConverter.printDate(mpx.getDeadline())); xml.setDuration(DatatypeConverter.printDurationMandatory(this, mpx.getDuration())); xml.setDurationText(mpx.getDurationText()); xml.setDurationFormat(DatatypeConverter.printDurationTimeUnits(mpx.getDuration(), mpx.getEstimated())); xml.setEarlyFinish(DatatypeConverter.printDate(mpx.getEarlyFinish())); xml.setEarlyStart(DatatypeConverter.printDate(mpx.getEarlyStart())); xml.setEarnedValueMethod(DatatypeConverter.printEarnedValueMethod(mpx.getEarnedValueMethod())); xml.setEffortDriven(Boolean.valueOf(mpx.getEffortDriven())); xml.setEstimated(Boolean.valueOf(mpx.getEstimated())); xml.setExternalTask(Boolean.valueOf(mpx.getExternalTask())); xml.setExternalTaskProject(mpx.getProject()); xml.setFinish(DatatypeConverter.printDate(mpx.getFinish())); xml.setFinishText(mpx.getFinishText()); xml.setFinishVariance(DatatypeConverter.printDurationInIntegerThousandthsOfMinutes(mpx.getFinishVariance())); xml.setFixedCost(DatatypeConverter.printCurrency(mpx.getFixedCost())); AccrueType fixedCostAccrual = mpx.getFixedCostAccrual(); if (fixedCostAccrual == null) { fixedCostAccrual = AccrueType.PRORATED; } xml.setFixedCostAccrual(fixedCostAccrual); // This is not correct //xml.setFreeSlack(BigInteger.valueOf((long)DatatypeConverter.printDurationInMinutes(mpx.getFreeSlack())*1000)); //xml.setFreeSlack(BIGINTEGER_ZERO); xml.setHideBar(Boolean.valueOf(mpx.getHideBar())); xml.setIsNull(Boolean.valueOf(mpx.getNull())); xml.setIsSubproject(Boolean.valueOf(mpx.getSubProject() != null)); xml.setIsSubprojectReadOnly(Boolean.valueOf(mpx.getSubprojectReadOnly())); xml.setHyperlink(mpx.getHyperlink()); xml.setHyperlinkAddress(mpx.getHyperlinkAddress()); xml.setHyperlinkSubAddress(mpx.getHyperlinkSubAddress()); xml.setID(NumberHelper.getBigInteger(mpx.getID())); xml.setIgnoreResourceCalendar(Boolean.valueOf(mpx.getIgnoreResourceCalendar())); xml.setLateFinish(DatatypeConverter.printDate(mpx.getLateFinish())); xml.setLateStart(DatatypeConverter.printDate(mpx.getLateStart())); xml.setLevelAssignments(Boolean.valueOf(mpx.getLevelAssignments())); xml.setLevelingCanSplit(Boolean.valueOf(mpx.getLevelingCanSplit())); if (mpx.getLevelingDelay() != null) { Duration levelingDelay = mpx.getLevelingDelay(); double tenthMinutes = 10.0 * Duration.convertUnits(levelingDelay.getDuration(), levelingDelay.getUnits(), TimeUnit.MINUTES, m_projectFile.getProjectProperties()).getDuration(); xml.setLevelingDelay(BigInteger.valueOf((long) tenthMinutes)); xml.setLevelingDelayFormat(DatatypeConverter.printDurationTimeUnits(levelingDelay, false)); } xml.setManual(Boolean.valueOf(mpx.getTaskMode() == TaskMode.MANUALLY_SCHEDULED)); if (mpx.getTaskMode() == TaskMode.MANUALLY_SCHEDULED) { xml.setManualDuration(DatatypeConverter.printDuration(this, mpx.getDuration())); xml.setManualFinish(DatatypeConverter.printDate(mpx.getFinish())); xml.setManualStart(DatatypeConverter.printDate(mpx.getStart())); } xml.setMilestone(Boolean.valueOf(mpx.getMilestone())); xml.setName(mpx.getName()); if (!mpx.getNotes().isEmpty()) { xml.setNotes(mpx.getNotes()); } xml.setOutlineLevel(NumberHelper.getBigInteger(mpx.getOutlineLevel())); xml.setOutlineNumber(mpx.getOutlineNumber()); xml.setOverAllocated(Boolean.valueOf(mpx.getOverAllocated())); xml.setOvertimeCost(DatatypeConverter.printCurrency(mpx.getOvertimeCost())); xml.setOvertimeWork(DatatypeConverter.printDuration(this, mpx.getOvertimeWork())); xml.setPercentComplete(NumberHelper.getBigInteger(mpx.getPercentageComplete())); xml.setPercentWorkComplete(NumberHelper.getBigInteger(mpx.getPercentageWorkComplete())); xml.setPhysicalPercentComplete(NumberHelper.getBigInteger(mpx.getPhysicalPercentComplete())); xml.setPriority(DatatypeConverter.printPriority(mpx.getPriority())); xml.setRecurring(Boolean.valueOf(mpx.getRecurring())); xml.setRegularWork(DatatypeConverter.printDuration(this, mpx.getRegularWork())); xml.setRemainingCost(DatatypeConverter.printCurrency(mpx.getRemainingCost())); if (mpx.getRemainingDuration() == null) { Duration duration = mpx.getDuration(); if (duration != null) { double amount = duration.getDuration(); amount -= ((amount * NumberHelper.getDouble(mpx.getPercentageComplete())) / 100); xml.setRemainingDuration(DatatypeConverter.printDuration(this, Duration.getInstance(amount, duration.getUnits()))); } } else { xml.setRemainingDuration(DatatypeConverter.printDuration(this, mpx.getRemainingDuration())); } xml.setRemainingOvertimeCost(DatatypeConverter.printCurrency(mpx.getRemainingOvertimeCost())); xml.setRemainingOvertimeWork(DatatypeConverter.printDuration(this, mpx.getRemainingOvertimeWork())); xml.setRemainingWork(DatatypeConverter.printDuration(this, mpx.getRemainingWork())); xml.setResume(DatatypeConverter.printDate(mpx.getResume())); xml.setResumeValid(Boolean.valueOf(mpx.getResumeValid())); xml.setRollup(Boolean.valueOf(mpx.getRollup())); xml.setStart(DatatypeConverter.printDate(mpx.getStart())); xml.setStartText(mpx.getStartText()); xml.setStartVariance(DatatypeConverter.printDurationInIntegerThousandthsOfMinutes(mpx.getStartVariance())); xml.setStop(DatatypeConverter.printDate(mpx.getStop())); xml.setSubprojectName(mpx.getSubprojectName()); xml.setSummary(Boolean.valueOf(mpx.getSummary())); xml.setTotalSlack(DatatypeConverter.printDurationInIntegerThousandthsOfMinutes(mpx.getTotalSlack())); xml.setType(mpx.getType()); xml.setUID(mpx.getUniqueID()); xml.setWBS(mpx.getWBS()); xml.setWBSLevel(mpx.getWBSLevel()); xml.setWork(DatatypeConverter.printDuration(this, mpx.getWork())); xml.setWorkVariance(DatatypeConverter.printDurationInDecimalThousandthsOfMinutes(mpx.getWorkVariance())); if (mpx.getTaskMode() == TaskMode.MANUALLY_SCHEDULED) { xml.setManualDuration(DatatypeConverter.printDuration(this, mpx.getManualDuration())); } writePredecessors(xml, mpx); writeTaskExtendedAttributes(xml, mpx); writeTaskBaselines(xml, mpx); return (xml); } /** * Writes task baseline data. * * @param xmlTask MSPDI task * @param mpxjTask MPXJ task */ private void writeTaskBaselines(Project.Tasks.Task xmlTask, Task mpxjTask) { Project.Tasks.Task.Baseline baseline = m_factory.createProjectTasksTaskBaseline(); boolean populated = false; Number cost = mpxjTask.getBaselineCost(); if (cost != null && cost.intValue() != 0) { populated = true; baseline.setCost(DatatypeConverter.printCurrency(cost)); } Duration duration = mpxjTask.getBaselineDuration(); if (duration != null && duration.getDuration() != 0) { populated = true; baseline.setDuration(DatatypeConverter.printDuration(this, duration)); baseline.setDurationFormat(DatatypeConverter.printDurationTimeUnits(duration, false)); } Date date = mpxjTask.getBaselineFinish(); if (date != null) { populated = true; baseline.setFinish(DatatypeConverter.printDate(date)); } date = mpxjTask.getBaselineStart(); if (date != null) { populated = true; baseline.setStart(DatatypeConverter.printDate(date)); } duration = mpxjTask.getBaselineWork(); if (duration != null && duration.getDuration() != 0) { populated = true; baseline.setWork(DatatypeConverter.printDuration(this, duration)); } if (populated) { baseline.setNumber(BigInteger.ZERO); xmlTask.getBaseline().add(baseline); } for (int loop = 1; loop <= 10; loop++) { baseline = m_factory.createProjectTasksTaskBaseline(); populated = false; cost = mpxjTask.getBaselineCost(loop); if (cost != null && cost.intValue() != 0) { populated = true; baseline.setCost(DatatypeConverter.printCurrency(cost)); } duration = mpxjTask.getBaselineDuration(loop); if (duration != null && duration.getDuration() != 0) { populated = true; baseline.setDuration(DatatypeConverter.printDuration(this, duration)); baseline.setDurationFormat(DatatypeConverter.printDurationTimeUnits(duration, false)); } date = mpxjTask.getBaselineFinish(loop); if (date != null) { populated = true; baseline.setFinish(DatatypeConverter.printDate(date)); } date = mpxjTask.getBaselineStart(loop); if (date != null) { populated = true; baseline.setStart(DatatypeConverter.printDate(date)); } duration = mpxjTask.getBaselineWork(loop); if (duration != null && duration.getDuration() != 0) { populated = true; baseline.setWork(DatatypeConverter.printDuration(this, duration)); } if (populated) { baseline.setNumber(BigInteger.valueOf(loop)); xmlTask.getBaseline().add(baseline); } } } /** * This method writes extended attribute data for a task. * * @param xml MSPDI task * @param mpx MPXJ task */ private void writeTaskExtendedAttributes(Project.Tasks.Task xml, Task mpx) { Project.Tasks.Task.ExtendedAttribute attrib; List<Project.Tasks.Task.ExtendedAttribute> extendedAttributes = xml.getExtendedAttribute(); for (TaskField mpxFieldID : getAllTaskExtendedAttributes()) { Object value = mpx.getCachedValue(mpxFieldID); if (writeExtendedAttribute(value, mpxFieldID)) { m_extendedAttributesInUse.add(mpxFieldID); Integer xmlFieldID = Integer.valueOf(MPPTaskField.getID(mpxFieldID) | MPPTaskField.TASK_FIELD_BASE); attrib = m_factory.createProjectTasksTaskExtendedAttribute(); extendedAttributes.add(attrib); attrib.setFieldID(xmlFieldID.toString()); attrib.setValue(DatatypeConverter.printExtendedAttribute(this, value, mpxFieldID.getDataType())); attrib.setDurationFormat(printExtendedAttributeDurationFormat(value)); } } } /** * Converts a duration to duration time units. * * @param value duration value * @return duration time units */ private BigInteger printExtendedAttributeDurationFormat(Object value) { BigInteger result = null; if (value instanceof Duration) { result = DatatypeConverter.printDurationTimeUnits(((Duration) value).getUnits(), false); } return (result); } /** * This method retrieves the UID for a calendar associated with a task. * * @param mpx MPX Task instance * @return calendar UID */ private BigInteger getTaskCalendarID(Task mpx) { BigInteger result = null; ProjectCalendar cal = mpx.getCalendar(); if (cal != null) { result = NumberHelper.getBigInteger(cal.getUniqueID()); } else { result = BigInteger.valueOf(-1); } return (result); } /** * This method writes predecessor data to an MSPDI file. * We have to deal with a slight anomaly in this method that is introduced * by the MPX file format. It would be possible for someone to create an * MPX file with both the predecessor list and the unique ID predecessor * list populated... which means that we must process both and avoid adding * duplicate predecessors. Also interesting to note is that MSP98 populates * the predecessor list, not the unique ID predecessor list, as you might * expect. * * @param xml MSPDI task data * @param mpx MPX task data */ private void writePredecessors(Project.Tasks.Task xml, Task mpx) { List<Project.Tasks.Task.PredecessorLink> list = xml.getPredecessorLink(); List<Relation> predecessors = mpx.getPredecessors(); if (predecessors != null) { for (Relation rel : predecessors) { Integer taskUniqueID = rel.getTargetTask().getUniqueID(); list.add(writePredecessor(taskUniqueID, rel.getType(), rel.getLag())); m_eventManager.fireRelationWrittenEvent(rel); } } } /** * This method writes a single predecessor link to the MSPDI file. * * @param taskID The task UID * @param type The predecessor type * @param lag The lag duration * @return A new link to be added to the MSPDI file */ private Project.Tasks.Task.PredecessorLink writePredecessor(Integer taskID, RelationType type, Duration lag) { Project.Tasks.Task.PredecessorLink link = m_factory.createProjectTasksTaskPredecessorLink(); link.setPredecessorUID(NumberHelper.getBigInteger(taskID)); link.setType(BigInteger.valueOf(type.getValue())); if (lag != null && lag.getDuration() != 0) { double linkLag = lag.getDuration(); if (lag.getUnits() != TimeUnit.PERCENT) { linkLag = 10.0 * Duration.convertUnits(linkLag, lag.getUnits(), TimeUnit.MINUTES, m_projectFile.getProjectProperties()).getDuration(); } link.setLinkLag(BigInteger.valueOf((long) linkLag)); link.setLagFormat(DatatypeConverter.printDurationTimeUnits(lag.getUnits(), false)); } return (link); } /** * This method writes assignment data to an MSPDI file. * * @param project Root node of the MSPDI file */ private void writeAssignments(Project project) { Project.Assignments assignments = m_factory.createProjectAssignments(); project.setAssignments(assignments); List<Project.Assignments.Assignment> list = assignments.getAssignment(); for (ResourceAssignment assignment : m_projectFile.getAllResourceAssignments()) { list.add(writeAssignment(assignment)); } // // Check to see if we have any tasks that have a percent complete value // but do not have resource assignments. If any exist, then we must // write a dummy resource assignment record to ensure that the MSPDI // file shows the correct percent complete amount for the task. // ProjectConfig config = m_projectFile.getProjectConfig(); boolean autoUniqueID = config.getAutoAssignmentUniqueID(); if (!autoUniqueID) { config.setAutoAssignmentUniqueID(true); } for (Task task : m_projectFile.getAllTasks()) { double percentComplete = NumberHelper.getDouble(task.getPercentageComplete()); if (percentComplete != 0 && task.getResourceAssignments().isEmpty() == true) { ResourceAssignment dummy = m_projectFile.newResourceAssignment(task); Duration duration = task.getDuration(); if (duration == null) { duration = Duration.getInstance(0, TimeUnit.HOURS); } double durationValue = duration.getDuration(); TimeUnit durationUnits = duration.getUnits(); double actualWork = (durationValue * percentComplete) / 100; double remainingWork = durationValue - actualWork; dummy.setResourceUniqueID(NULL_RESOURCE_ID); dummy.setWork(duration); dummy.setActualWork(Duration.getInstance(actualWork, durationUnits)); dummy.setRemainingWork(Duration.getInstance(remainingWork, durationUnits)); list.add(writeAssignment(dummy)); } } config.setAutoAssignmentUniqueID(autoUniqueID); } /** * This method writes data for a single assignment to an MSPDI file. * * @param mpx Resource assignment data * @return New MSPDI assignment instance */ protected Project.Assignments.Assignment writeAssignment(ResourceAssignment mpx) //claur changed to protected { Project.Assignments.Assignment xml = m_factory.createProjectAssignmentsAssignment(); xml.setActualCost(DatatypeConverter.printCurrency(mpx.getActualCost())); xml.setActualFinish(DatatypeConverter.printDate(mpx.getActualFinish())); xml.setActualOvertimeCost(DatatypeConverter.printCurrency(mpx.getActualOvertimeCost())); xml.setActualOvertimeWork(DatatypeConverter.printDuration(this, mpx.getActualOvertimeWork())); xml.setActualStart(DatatypeConverter.printDate(mpx.getActualStart())); xml.setActualWork(DatatypeConverter.printDuration(this, mpx.getActualWork())); xml.setACWP(DatatypeConverter.printCurrency(mpx.getACWP())); xml.setBCWP(DatatypeConverter.printCurrency(mpx.getBCWP())); xml.setBCWS(DatatypeConverter.printCurrency(mpx.getBCWS())); xml.setBudgetCost(DatatypeConverter.printCurrency(mpx.getBudgetCost())); xml.setBudgetWork(DatatypeConverter.printDuration(this, mpx.getBudgetWork())); xml.setCost(DatatypeConverter.printCurrency(mpx.getCost())); if (mpx.getCostRateTableIndex() != 0) { xml.setCostRateTable(BigInteger.valueOf(mpx.getCostRateTableIndex())); } xml.setCreationDate(DatatypeConverter.printDate(mpx.getCreateDate())); xml.setCV(DatatypeConverter.printCurrency(mpx.getCV())); xml.setDelay(DatatypeConverter.printDurationInIntegerTenthsOfMinutes(mpx.getDelay())); xml.setFinish(DatatypeConverter.printDate(mpx.getFinish())); xml.setHasFixedRateUnits(Boolean.valueOf(mpx.getVariableRateUnits() == null)); xml.setFixedMaterial(Boolean.valueOf(mpx.getResource() != null && mpx.getResource().getType() == ResourceType.MATERIAL)); xml.setHyperlink(mpx.getHyperlink()); xml.setHyperlinkAddress(mpx.getHyperlinkAddress()); xml.setHyperlinkSubAddress(mpx.getHyperlinkSubAddress()); xml.setLevelingDelay(DatatypeConverter.printDurationInIntegerTenthsOfMinutes(mpx.getLevelingDelay())); xml.setLevelingDelayFormat(DatatypeConverter.printDurationTimeUnits(mpx.getLevelingDelay(), false)); if (!mpx.getNotes().isEmpty()) { xml.setNotes(mpx.getNotes()); } xml.setOvertimeCost(DatatypeConverter.printCurrency(mpx.getOvertimeCost())); xml.setOvertimeWork(DatatypeConverter.printDuration(this, mpx.getOvertimeWork())); xml.setPercentWorkComplete(NumberHelper.getBigInteger(mpx.getPercentageWorkComplete())); xml.setRateScale(mpx.getVariableRateUnits() == null ? null : DatatypeConverter.printTimeUnit(mpx.getVariableRateUnits())); xml.setRegularWork(DatatypeConverter.printDuration(this, mpx.getRegularWork())); xml.setRemainingCost(DatatypeConverter.printCurrency(mpx.getRemainingCost())); xml.setRemainingOvertimeCost(DatatypeConverter.printCurrency(mpx.getRemainingOvertimeCost())); xml.setRemainingOvertimeWork(DatatypeConverter.printDuration(this, mpx.getRemainingOvertimeWork())); xml.setRemainingWork(DatatypeConverter.printDuration(this, mpx.getRemainingWork())); xml.setResourceUID(mpx.getResource() == null ? BigInteger.valueOf(NULL_RESOURCE_ID.intValue()) : BigInteger.valueOf(NumberHelper.getInt(mpx.getResourceUniqueID()))); xml.setStart(DatatypeConverter.printDate(mpx.getStart())); xml.setSV(DatatypeConverter.printCurrency(mpx.getSV())); xml.setTaskUID(NumberHelper.getBigInteger(mpx.getTask().getUniqueID())); xml.setUID(NumberHelper.getBigInteger(mpx.getUniqueID())); xml.setUnits(DatatypeConverter.printUnits(mpx.getUnits())); xml.setVAC(DatatypeConverter.printCurrency(mpx.getVAC())); xml.setWork(DatatypeConverter.printDuration(this, mpx.getWork())); xml.setWorkContour(mpx.getWorkContour()); xml.setCostVariance(DatatypeConverter.printCurrency(mpx.getCostVariance())); xml.setWorkVariance(DatatypeConverter.printDurationInDecimalThousandthsOfMinutes(mpx.getWorkVariance())); xml.setStartVariance(DatatypeConverter.printDurationInIntegerThousandthsOfMinutes(mpx.getStartVariance())); xml.setFinishVariance(DatatypeConverter.printDurationInIntegerThousandthsOfMinutes(mpx.getFinishVariance())); writeAssignmentBaselines(xml, mpx); writeAssignmentExtendedAttributes(xml, mpx); writeAssignmentTimephasedData(mpx, xml); m_eventManager.fireAssignmentWrittenEvent(mpx); return (xml); } /** * Writes assignment baseline data. * * @param xml MSPDI assignment * @param mpxj MPXJ assignment */ private void writeAssignmentBaselines(Project.Assignments.Assignment xml, ResourceAssignment mpxj) { Project.Assignments.Assignment.Baseline baseline = m_factory.createProjectAssignmentsAssignmentBaseline(); boolean populated = false; Number cost = mpxj.getBaselineCost(); if (cost != null && cost.intValue() != 0) { populated = true; baseline.setCost(DatatypeConverter.printExtendedAttributeCurrency(cost)); } Date date = mpxj.getBaselineFinish(); if (date != null) { populated = true; baseline.setFinish(DatatypeConverter.printExtendedAttributeDate(date)); } date = mpxj.getBaselineStart(); if (date != null) { populated = true; baseline.setStart(DatatypeConverter.printExtendedAttributeDate(date)); } Duration duration = mpxj.getBaselineWork(); if (duration != null && duration.getDuration() != 0) { populated = true; baseline.setWork(DatatypeConverter.printDuration(this, duration)); } if (populated) { baseline.setNumber("0"); xml.getBaseline().add(baseline); } for (int loop = 1; loop <= 10; loop++) { baseline = m_factory.createProjectAssignmentsAssignmentBaseline(); populated = false; cost = mpxj.getBaselineCost(loop); if (cost != null && cost.intValue() != 0) { populated = true; baseline.setCost(DatatypeConverter.printExtendedAttributeCurrency(cost)); } date = mpxj.getBaselineFinish(loop); if (date != null) { populated = true; baseline.setFinish(DatatypeConverter.printExtendedAttributeDate(date)); } date = mpxj.getBaselineStart(loop); if (date != null) { populated = true; baseline.setStart(DatatypeConverter.printExtendedAttributeDate(date)); } duration = mpxj.getBaselineWork(loop); if (duration != null && duration.getDuration() != 0) { populated = true; baseline.setWork(DatatypeConverter.printDuration(this, duration)); } if (populated) { baseline.setNumber(Integer.toString(loop)); xml.getBaseline().add(baseline); } } } /** * This method writes extended attribute data for an assignment. * * @param xml MSPDI assignment * @param mpx MPXJ assignment */ private void writeAssignmentExtendedAttributes(Project.Assignments.Assignment xml, ResourceAssignment mpx) { Project.Assignments.Assignment.ExtendedAttribute attrib; List<Project.Assignments.Assignment.ExtendedAttribute> extendedAttributes = xml.getExtendedAttribute(); for (AssignmentField mpxFieldID : getAllAssignmentExtendedAttributes()) { Object value = mpx.getCachedValue(mpxFieldID); if (writeExtendedAttribute(value, mpxFieldID)) { m_extendedAttributesInUse.add(mpxFieldID); Integer xmlFieldID = Integer.valueOf(MPPAssignmentField.getID(mpxFieldID) | MPPAssignmentField.ASSIGNMENT_FIELD_BASE); attrib = m_factory.createProjectAssignmentsAssignmentExtendedAttribute(); extendedAttributes.add(attrib); attrib.setFieldID(xmlFieldID.toString()); attrib.setValue(DatatypeConverter.printExtendedAttribute(this, value, mpxFieldID.getDataType())); attrib.setDurationFormat(printExtendedAttributeDurationFormat(value)); } } } /** * Writes the timephased data for a resource assignment. * * @param mpx MPXJ assignment * @param xml MSDPI assignment */ private void writeAssignmentTimephasedData(ResourceAssignment mpx, Project.Assignments.Assignment xml) { if (m_writeTimphasedData && mpx.getHasTimephasedData()) { List<TimephasedDataType> list = xml.getTimephasedData(); ProjectCalendar calendar = mpx.getCalendar(); BigInteger assignmentID = xml.getUID(); List<TimephasedWork> complete = mpx.getTimephasedActualWork(); List<TimephasedWork> planned = mpx.getTimephasedWork(); if (m_splitTimephasedAsDays) { TimephasedWork lastComplete = null; if (!complete.isEmpty()) { lastComplete = complete.get(complete.size() - 1); } TimephasedWork firstPlanned = null; if (!planned.isEmpty()) { firstPlanned = planned.get(0); } planned = splitDays(calendar, mpx.getTimephasedWork(), null, lastComplete); complete = splitDays(calendar, complete, firstPlanned, null); } writeAssignmentTimephasedData(assignmentID, list, planned, 1); writeAssignmentTimephasedData(assignmentID, list, complete, 2); } } /** * Splits timephased data into individual days. * * @param calendar current calendar * @param list list of timephased assignment data * @param first first planned assignment * @param last last completed assignment * @return list of timephased data ready for output */ private List<TimephasedWork> splitDays(ProjectCalendar calendar, List<TimephasedWork> list, TimephasedWork first, TimephasedWork last) { List<TimephasedWork> result = new LinkedList<TimephasedWork>(); for (TimephasedWork assignment : list) { Date startDate = assignment.getStart(); Date finishDate = assignment.getFinish(); Date startDay = DateHelper.getDayStartDate(startDate); Date finishDay = DateHelper.getDayStartDate(finishDate); if (startDay.getTime() == finishDay.getTime()) { Date startTime = calendar.getStartTime(startDay); Date currentStart = DateHelper.setTime(startDay, startTime); if (startDate.getTime() > currentStart.getTime()) { boolean paddingRequired = true; if (last != null) { Date lastFinish = last.getFinish(); if (lastFinish.getTime() == startDate.getTime()) { paddingRequired = false; } else { Date lastFinishDay = DateHelper.getDayStartDate(lastFinish); if (startDay.getTime() == lastFinishDay.getTime()) { currentStart = lastFinish; } } } if (paddingRequired) { Duration zeroHours = Duration.getInstance(0, TimeUnit.HOURS); TimephasedWork padding = new TimephasedWork(); padding.setStart(currentStart); padding.setFinish(startDate); padding.setTotalAmount(zeroHours); padding.setAmountPerDay(zeroHours); result.add(padding); } } result.add(assignment); Date endTime = calendar.getFinishTime(startDay); Date currentFinish = DateHelper.setTime(startDay, endTime); if (finishDate.getTime() < currentFinish.getTime()) { boolean paddingRequired = true; if (first != null) { Date firstStart = first.getStart(); if (firstStart.getTime() == finishDate.getTime()) { paddingRequired = false; } else { Date firstStartDay = DateHelper.getDayStartDate(firstStart); if (finishDay.getTime() == firstStartDay.getTime()) { currentFinish = firstStart; } } } if (paddingRequired) { Duration zeroHours = Duration.getInstance(0, TimeUnit.HOURS); TimephasedWork padding = new TimephasedWork(); padding.setStart(finishDate); padding.setFinish(currentFinish); padding.setTotalAmount(zeroHours); padding.setAmountPerDay(zeroHours); result.add(padding); } } } else { Date currentStart = startDate; Calendar cal = Calendar.getInstance(); boolean isWorking = calendar.isWorkingDate(currentStart); while (currentStart.getTime() < finishDate.getTime()) { if (isWorking) { Date endTime = calendar.getFinishTime(currentStart); Date currentFinish = DateHelper.setTime(currentStart, endTime); if (currentFinish.getTime() > finishDate.getTime()) { currentFinish = finishDate; } TimephasedWork split = new TimephasedWork(); split.setStart(currentStart); split.setFinish(currentFinish); split.setTotalAmount(assignment.getAmountPerDay()); split.setAmountPerDay(assignment.getAmountPerDay()); result.add(split); } cal.setTime(currentStart); cal.add(Calendar.DAY_OF_YEAR, 1); currentStart = cal.getTime(); isWorking = calendar.isWorkingDate(currentStart); if (isWorking) { Date startTime = calendar.getStartTime(currentStart); DateHelper.setTime(cal, startTime); currentStart = cal.getTime(); } } } } return result; } /** * Writes a list of timephased data to the MSPDI file. * * @param assignmentID current assignment ID * @param list output list of timephased data items * @param data input list of timephased data * @param type list type (planned or completed) */ private void writeAssignmentTimephasedData(BigInteger assignmentID, List<TimephasedDataType> list, List<TimephasedWork> data, int type) { for (TimephasedWork mpx : data) { TimephasedDataType xml = m_factory.createTimephasedDataType(); list.add(xml); xml.setStart(DatatypeConverter.printDate(mpx.getStart())); xml.setFinish(DatatypeConverter.printDate(mpx.getFinish())); xml.setType(BigInteger.valueOf(type)); xml.setUID(assignmentID); xml.setUnit(DatatypeConverter.printDurationTimeUnits(mpx.getTotalAmount(), false)); xml.setValue(DatatypeConverter.printDuration(this, mpx.getTotalAmount())); } } /** * Retrieve list of assignment extended attributes. * * @return list of extended attributes */ private List<AssignmentField> getAllAssignmentExtendedAttributes() { ArrayList<AssignmentField> result = new ArrayList<AssignmentField>(); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_COST)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_DATE)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_DURATION)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_COST)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_DATE)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_DURATION)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_FLAG)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_NUMBER)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_RESOURCE_MULTI_VALUE)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_RESOURCE_OUTLINE_CODE)); result.addAll(Arrays.asList(AssignmentFieldLists.ENTERPRISE_TEXT)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_FINISH)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_FLAG)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_NUMBER)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_START)); result.addAll(Arrays.asList(AssignmentFieldLists.CUSTOM_TEXT)); return result; } /** * Retrieve list of task extended attributes. * * @return list of extended attributes */ private List<TaskField> getAllTaskExtendedAttributes() { ArrayList<TaskField> result = new ArrayList<TaskField>(); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_TEXT)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_START)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_FINISH)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_COST)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_DATE)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_FLAG)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_NUMBER)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_DURATION)); result.addAll(Arrays.asList(TaskFieldLists.CUSTOM_OUTLINE_CODE)); result.addAll(Arrays.asList(TaskFieldLists.ENTERPRISE_COST)); result.addAll(Arrays.asList(TaskFieldLists.ENTERPRISE_DATE)); result.addAll(Arrays.asList(TaskFieldLists.ENTERPRISE_DURATION)); result.addAll(Arrays.asList(TaskFieldLists.ENTERPRISE_FLAG)); result.addAll(Arrays.asList(TaskFieldLists.ENTERPRISE_NUMBER)); result.addAll(Arrays.asList(TaskFieldLists.ENTERPRISE_TEXT)); return result; } /** * Retrieve list of resource extended attributes. * * @return list of extended attributes */ private List<ResourceField> getAllResourceExtendedAttributes() { ArrayList<ResourceField> result = new ArrayList<ResourceField>(); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_TEXT)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_START)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_FINISH)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_COST)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_DATE)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_FLAG)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_NUMBER)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_DURATION)); result.addAll(Arrays.asList(ResourceFieldLists.CUSTOM_OUTLINE_CODE)); result.addAll(Arrays.asList(ResourceFieldLists.ENTERPRISE_COST)); result.addAll(Arrays.asList(ResourceFieldLists.ENTERPRISE_DATE)); result.addAll(Arrays.asList(ResourceFieldLists.ENTERPRISE_DURATION)); result.addAll(Arrays.asList(ResourceFieldLists.ENTERPRISE_FLAG)); result.addAll(Arrays.asList(ResourceFieldLists.ENTERPRISE_NUMBER)); result.addAll(Arrays.asList(ResourceFieldLists.ENTERPRISE_TEXT)); return result; } /** * Package-private accessor method used to retrieve the project file * currently being processed by this writer. * * @return project file instance */ ProjectFile getProjectFile() { return (m_projectFile); } /** * Cached context to minimise construction cost. */ private static JAXBContext CONTEXT; /** * Note any error occurring during context construction. */ private static JAXBException CONTEXT_EXCEPTION; static { try { // // JAXB RI property to speed up construction // System.setProperty("com.sun.xml.bind.v2.runtime.JAXBContextImpl.fastBoot", "true"); // // Construct the context // CONTEXT = JAXBContext.newInstance("net.sf.mpxj.mspdi.schema", MSPDIWriter.class.getClassLoader()); } catch (JAXBException ex) { CONTEXT_EXCEPTION = ex; CONTEXT = null; } } protected ObjectFactory m_factory; //claur changed to protected protected ProjectFile m_projectFile;//claur need protected to add default assignment private EventManager m_eventManager; private Set<FieldType> m_extendedAttributesInUse; private boolean m_splitTimephasedAsDays = true; private boolean m_writeTimphasedData; private SaveVersion m_saveVersion = SaveVersion.Project2002; private static final BigInteger BIGINTEGER_ZERO = BigInteger.valueOf(0); private static final Integer NULL_RESOURCE_ID = Integer.valueOf(-65535); }