/* * file: MSPDIReader.java * author: Jon Iles * copyright: (c) Packwood Software 2005 * date: 30/12/2005 */ /* * 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.InputStream; import java.math.BigInteger; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; 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.Unmarshaller; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.sax.SAXSource; import net.sf.mpxj.AssignmentField; import net.sf.mpxj.Availability; import net.sf.mpxj.AvailabilityTable; import net.sf.mpxj.CostRateTable; import net.sf.mpxj.CostRateTableEntry; 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.MPXJException; 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.Rate; 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.SubProject; 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.BooleanHelper; import net.sf.mpxj.common.DefaultTimephasedWorkContainer; 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.Pair; import net.sf.mpxj.common.SplitTaskFactory; import net.sf.mpxj.common.TimephasedWorkNormaliser; import net.sf.mpxj.listener.ProjectListener; import net.sf.mpxj.mspdi.schema.Project; 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.WeekDays; import net.sf.mpxj.mspdi.schema.Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay; 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.reader.AbstractProjectReader; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; /** * This class creates a new ProjectFile instance by reading an MSPDI file. */ public class MSPDIReader extends AbstractProjectReader {//claur removed final to extend /** * {@inheritDoc} */ @Override public void addProjectListener(ProjectListener listener) { if (m_projectListeners == null) { m_projectListeners = new LinkedList<ProjectListener>(); } m_projectListeners.add(listener); } /** * {@inheritDoc} */ @Override public ProjectFile read(InputStream stream) throws MPXJException { try { m_projectFile = new ProjectFile(); m_eventManager = m_projectFile.getEventManager(); ProjectConfig config = m_projectFile.getProjectConfig(); config.setAutoTaskID(false); config.setAutoTaskUniqueID(false); config.setAutoResourceID(false); config.setAutoResourceUniqueID(false); config.setAutoOutlineLevel(false); config.setAutoOutlineNumber(false); config.setAutoWBS(false); config.setAutoCalendarUniqueID(false); config.setAutoAssignmentUniqueID(false); m_eventManager.addProjectListeners(m_projectListeners); SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); factory.setNamespaceAware(true); SAXParser saxParser = factory.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); SAXSource doc = new SAXSource(xmlReader, new InputSource(stream)); if (CONTEXT == null) { throw CONTEXT_EXCEPTION; } Unmarshaller unmarshaller = CONTEXT.createUnmarshaller(); // // If we are matching the behaviour of MS project, then we need to // ignore validation warnings. // if (m_compatibleInput == true) { unmarshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent event) { return (true); } }); } DatatypeConverter.setParentFile(m_projectFile); Project project = (Project) unmarshaller.unmarshal(doc); HashMap<BigInteger, ProjectCalendar> calendarMap = new HashMap<BigInteger, ProjectCalendar>(); readProjectProperties(project); readProjectExtendedAttributes(project); readCalendars(project, calendarMap); readResources(project, calendarMap); readTasks(project); readAssignments(project); // // Ensure that the unique ID counters are correct // config.updateUniqueCounters(); // // Ensure that the default calendar name is set in the project properties // ProjectCalendar defaultCalendar = calendarMap.get(project.getCalendarUID()); if (defaultCalendar != null) { m_projectFile.getProjectProperties().setDefaultCalendarName(defaultCalendar.getName()); } return (m_projectFile); } catch (ParserConfigurationException ex) { throw new MPXJException("Failed to parse file", ex); } catch (JAXBException ex) { throw new MPXJException("Failed to parse file", ex); } catch (SAXException ex) { throw new MPXJException("Failed to parse file", ex); } finally { m_projectFile = null; } } /** * This method extracts project properties from an MSPDI file. * * @param project Root node of the MSPDI file */ private void readProjectProperties(Project project) { ProjectProperties properties = m_projectFile.getProjectProperties(); properties.setActualsInSync(BooleanHelper.getBoolean(project.isActualsInSync())); properties.setAdminProject(BooleanHelper.getBoolean(project.isAdminProject())); properties.setAuthor(project.getAuthor()); properties.setAutoAddNewResourcesAndTasks(BooleanHelper.getBoolean(project.isAutoAddNewResourcesAndTasks())); properties.setAutolink(BooleanHelper.getBoolean(project.isAutolink())); properties.setBaselineForEarnedValue(NumberHelper.getInteger(project.getBaselineForEarnedValue())); properties.setDefaultCalendarName(project.getCalendarUID() == null ? null : project.getCalendarUID().toString()); properties.setCategory(project.getCategory()); properties.setCompany(project.getCompany()); properties.setCreationDate(DatatypeConverter.parseDate(project.getCreationDate())); properties.setCriticalSlackLimit(NumberHelper.getInteger(project.getCriticalSlackLimit())); properties.setCurrencyDigits(NumberHelper.getInteger(project.getCurrencyDigits())); properties.setCurrencyCode(project.getCurrencyCode()); properties.setCurrencySymbol(project.getCurrencySymbol()); properties.setCurrentDate(DatatypeConverter.parseDate(project.getCurrentDate())); properties.setDaysPerMonth(NumberHelper.getInteger(project.getDaysPerMonth())); properties.setDefaultDurationUnits(DatatypeConverter.parseDurationTimeUnits(project.getDurationFormat())); properties.setDefaultEndTime(DatatypeConverter.parseTime(project.getDefaultFinishTime())); properties.setDefaultFixedCostAccrual(project.getDefaultFixedCostAccrual()); properties.setDefaultOvertimeRate(DatatypeConverter.parseRate(project.getDefaultOvertimeRate())); properties.setDefaultStandardRate(DatatypeConverter.parseRate(project.getDefaultStandardRate())); properties.setDefaultStartTime(DatatypeConverter.parseTime(project.getDefaultStartTime())); properties.setDefaultTaskEarnedValueMethod(DatatypeConverter.parseEarnedValueMethod(project.getDefaultTaskEVMethod())); properties.setDefaultTaskType(project.getDefaultTaskType()); properties.setDefaultWorkUnits(DatatypeConverter.parseWorkUnits(project.getWorkFormat())); properties.setEarnedValueMethod(DatatypeConverter.parseEarnedValueMethod(project.getEarnedValueMethod())); properties.setEditableActualCosts(BooleanHelper.getBoolean(project.isEditableActualCosts())); properties.setExtendedCreationDate(DatatypeConverter.parseDate(project.getExtendedCreationDate())); properties.setFinishDate(DatatypeConverter.parseDate(project.getFinishDate())); properties.setFiscalYearStart(BooleanHelper.getBoolean(project.isFiscalYearStart())); properties.setFiscalYearStartMonth(NumberHelper.getInteger(project.getFYStartDate())); properties.setHonorConstraints(BooleanHelper.getBoolean(project.isHonorConstraints())); properties.setInsertedProjectsLikeSummary(BooleanHelper.getBoolean(project.isInsertedProjectsLikeSummary())); properties.setLastSaved(DatatypeConverter.parseDate(project.getLastSaved())); properties.setManager(project.getManager()); properties.setMicrosoftProjectServerURL(BooleanHelper.getBoolean(project.isMicrosoftProjectServerURL())); properties.setMinutesPerDay(NumberHelper.getInteger(project.getMinutesPerDay())); properties.setMinutesPerWeek(NumberHelper.getInteger(project.getMinutesPerWeek())); properties.setMoveCompletedEndsBack(BooleanHelper.getBoolean(project.isMoveCompletedEndsBack())); properties.setMoveCompletedEndsForward(BooleanHelper.getBoolean(project.isMoveCompletedEndsForward())); properties.setMoveRemainingStartsBack(BooleanHelper.getBoolean(project.isMoveRemainingStartsBack())); properties.setMoveRemainingStartsForward(BooleanHelper.getBoolean(project.isMoveRemainingStartsForward())); properties.setMultipleCriticalPaths(BooleanHelper.getBoolean(project.isMultipleCriticalPaths())); properties.setName(project.getName()); properties.setNewTasksEffortDriven(BooleanHelper.getBoolean(project.isNewTasksEffortDriven())); properties.setNewTasksEstimated(BooleanHelper.getBoolean(project.isNewTasksEstimated())); properties.setNewTaskStartIsProjectStart(NumberHelper.getInt(project.getNewTaskStartDate()) == 0); properties.setProjectExternallyEdited(BooleanHelper.getBoolean(project.isProjectExternallyEdited())); properties.setProjectTitle(project.getTitle()); properties.setRemoveFileProperties(BooleanHelper.getBoolean(project.isRemoveFileProperties())); properties.setRevision(NumberHelper.getInteger(project.getRevision())); properties.setScheduleFrom(BooleanHelper.getBoolean(project.isScheduleFromStart()) ? ScheduleFrom.START : ScheduleFrom.FINISH); properties.setSubject(project.getSubject()); properties.setSplitInProgressTasks(BooleanHelper.getBoolean(project.isSplitsInProgressTasks())); properties.setSpreadActualCost(BooleanHelper.getBoolean(project.isSpreadActualCost())); properties.setSpreadPercentComplete(BooleanHelper.getBoolean(project.isSpreadPercentComplete())); properties.setStartDate(DatatypeConverter.parseDate(project.getStartDate())); properties.setStatusDate(DatatypeConverter.parseDate(project.getStatusDate())); properties.setSymbolPosition(project.getCurrencySymbolPosition()); properties.setUniqueID(project.getUID()); properties.setUpdatingTaskStatusUpdatesResourceStatus(BooleanHelper.getBoolean(project.isTaskUpdatesResource())); properties.setWeekStartDay(DatatypeConverter.parseDay(project.getWeekStartDay())); } /** * This method extracts calendar data from an MSPDI file. * * @param project Root node of the MSPDI file * @param map Map of calendar UIDs to names */ private void readCalendars(Project project, HashMap<BigInteger, ProjectCalendar> map) { Project.Calendars calendars = project.getCalendars(); if (calendars != null) { LinkedList<Pair<ProjectCalendar, BigInteger>> baseCalendars = new LinkedList<Pair<ProjectCalendar, BigInteger>>(); for (Project.Calendars.Calendar cal : calendars.getCalendar()) { readCalendar(cal, map, baseCalendars); } updateBaseCalendarNames(baseCalendars, map); } try { ProjectProperties properties = m_projectFile.getProjectProperties(); BigInteger calendarID = new BigInteger(properties.getDefaultCalendarName()); ProjectCalendar calendar = map.get(calendarID); m_projectFile.setDefaultCalendar(calendar); } catch (Exception ex) { // Ignore exceptions } } /** * The way calendars are stored in an MSPDI file means that there * can be forward references between the base calendar unique ID for a * derived calendar, and the base calendar itself. To get around this, * we initially populate the base calendar name attribute with the * base calendar unique ID, and now in this method we can convert those * ID values into the correct names. * * @param baseCalendars list of calendars and base calendar IDs * @param map map of calendar ID values and calendar objects */ private static void updateBaseCalendarNames(List<Pair<ProjectCalendar, BigInteger>> baseCalendars, HashMap<BigInteger, ProjectCalendar> map) { for (Pair<ProjectCalendar, BigInteger> pair : baseCalendars) { ProjectCalendar cal = pair.getFirst(); BigInteger baseCalendarID = pair.getSecond(); ProjectCalendar baseCal = map.get(baseCalendarID); if (baseCal != null) { cal.setParent(baseCal); } } } /** * This method extracts data for a single calendar from an MSPDI file. * * @param calendar Calendar data * @param map Map of calendar UIDs to names * @param baseCalendars list of base calendars */ private void readCalendar(Project.Calendars.Calendar calendar, HashMap<BigInteger, ProjectCalendar> map, LinkedList<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)); } Project.Calendars.Calendar.WeekDays days = calendar.getWeekDays(); if (days != null) { for (Project.Calendars.Calendar.WeekDays.WeekDay weekDay : days.getWeekDay()) { readDay(bc, weekDay); } } 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); } readExceptions(calendar, bc); readWorkWeeks(calendar, bc); map.put(calendar.getUID(), bc); m_eventManager.fireCalendarReadEvent(bc); } /** * This method extracts data for a single day from an MSPDI file. * * @param calendar Calendar data * @param day Day data */ private void readDay(ProjectCalendar calendar, Project.Calendars.Calendar.WeekDays.WeekDay day) { BigInteger dayType = day.getDayType(); if (dayType != null) { if (dayType.intValue() == 0) { readExceptionDay(calendar, day); } else { readNormalDay(calendar, day); } } } /** * This method extracts data for a normal working day from an MSPDI file. * * @param calendar Calendar data * @param weekDay Day data */ private void readNormalDay(ProjectCalendar calendar, Project.Calendars.Calendar.WeekDays.WeekDay weekDay) { int dayNumber = weekDay.getDayType().intValue(); Day day = Day.getInstance(dayNumber); calendar.setWorkingDay(day, BooleanHelper.getBoolean(weekDay.isDayWorking())); ProjectCalendarHours hours = calendar.addCalendarHours(day); Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes times = weekDay.getWorkingTimes(); if (times != null) { for (Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime period : times.getWorkingTime()) { Date startTime = DatatypeConverter.parseTime(period.getFromTime()); Date endTime = DatatypeConverter.parseTime(period.getToTime()); if (startTime != null && endTime != null) { if (startTime.getTime() >= endTime.getTime()) { Calendar cal = Calendar.getInstance(); cal.setTime(endTime); cal.add(Calendar.DAY_OF_YEAR, 1); endTime = cal.getTime(); } hours.addRange(new DateRange(startTime, endTime)); } } } } /** * This method extracts data for an exception day from an MSPDI file. * * @param calendar Calendar data * @param day Day data */ private void readExceptionDay(ProjectCalendar calendar, Project.Calendars.Calendar.WeekDays.WeekDay day) { Project.Calendars.Calendar.WeekDays.WeekDay.TimePeriod timePeriod = day.getTimePeriod(); Date fromDate = DatatypeConverter.parseDate(timePeriod.getFromDate()); Date toDate = DatatypeConverter.parseDate(timePeriod.getToDate()); Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes times = day.getWorkingTimes(); ProjectCalendarException exception = calendar.addCalendarException(fromDate, toDate); if (times != null) { List<Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime> time = times.getWorkingTime(); for (Project.Calendars.Calendar.WeekDays.WeekDay.WorkingTimes.WorkingTime period : time) { Date startTime = DatatypeConverter.parseTime(period.getFromTime()); Date endTime = DatatypeConverter.parseTime(period.getToTime()); if (startTime != null && endTime != null) { if (startTime.getTime() >= endTime.getTime()) { Calendar cal = Calendar.getInstance(); cal.setTime(endTime); cal.add(Calendar.DAY_OF_YEAR, 1); endTime = cal.getTime(); } exception.addRange(new DateRange(startTime, endTime)); } } } } /** * Reads any exceptions present in the file. This is only used in MSPDI * file versions saved by Project 2007 and later. * * @param calendar XML calendar * @param bc MPXJ calendar */ private void readExceptions(Project.Calendars.Calendar calendar, ProjectCalendar bc) { Project.Calendars.Calendar.Exceptions exceptions = calendar.getExceptions(); if (exceptions != null) { for (Project.Calendars.Calendar.Exceptions.Exception exception : exceptions.getException()) { Date fromDate = DatatypeConverter.parseDate(exception.getTimePeriod().getFromDate()); Date toDate = DatatypeConverter.parseDate(exception.getTimePeriod().getToDate()); ProjectCalendarException bce = bc.addCalendarException(fromDate, toDate); 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 = DatatypeConverter.parseTime(period.getFromTime()); Date endTime = DatatypeConverter.parseTime(period.getToTime()); if (startTime != null && endTime != null) { if (startTime.getTime() >= endTime.getTime()) { Calendar cal = Calendar.getInstance(); cal.setTime(endTime); cal.add(Calendar.DAY_OF_YEAR, 1); endTime = cal.getTime(); } bce.addRange(new DateRange(startTime, endTime)); } } } } } } /** * Read the work weeks associated with this calendar. * * @param xmlCalendar XML calendar object * @param mpxjCalendar MPXJ calendar object */ private void readWorkWeeks(Project.Calendars.Calendar xmlCalendar, ProjectCalendar mpxjCalendar) { WorkWeeks ww = xmlCalendar.getWorkWeeks(); if (ww != null) { for (WorkWeek xmlWeek : ww.getWorkWeek()) { ProjectCalendarWeek week = mpxjCalendar.addWorkWeek(); week.setName(xmlWeek.getName()); Date startTime = DatatypeConverter.parseDate(xmlWeek.getTimePeriod().getFromDate()); Date endTime = DatatypeConverter.parseDate(xmlWeek.getTimePeriod().getToDate()); week.setDateRange(new DateRange(startTime, endTime)); WeekDays xmlWeekDays = xmlWeek.getWeekDays(); if (xmlWeekDays != null) { for (WeekDay xmlWeekDay : xmlWeekDays.getWeekDay()) { int dayNumber = xmlWeekDay.getDayType().intValue(); Day day = Day.getInstance(dayNumber); week.setWorkingDay(day, BooleanHelper.getBoolean(xmlWeekDay.isDayWorking())); ProjectCalendarHours hours = week.addCalendarHours(day); Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay.WorkingTimes times = xmlWeekDay.getWorkingTimes(); if (times != null) { for (Project.Calendars.Calendar.WorkWeeks.WorkWeek.WeekDays.WeekDay.WorkingTimes.WorkingTime period : times.getWorkingTime()) { startTime = DatatypeConverter.parseTime(period.getFromTime()); endTime = DatatypeConverter.parseTime(period.getToTime()); if (startTime != null && endTime != null) { if (startTime.getTime() >= endTime.getTime()) { Calendar cal = Calendar.getInstance(); cal.setTime(endTime); cal.add(Calendar.DAY_OF_YEAR, 1); endTime = cal.getTime(); } hours.addRange(new DateRange(startTime, endTime)); } } } } } } } } /** * This method extracts project extended attribute data from an MSPDI file. * * @param project Root node of the MSPDI file */ private void readProjectExtendedAttributes(Project project) { Project.ExtendedAttributes attributes = project.getExtendedAttributes(); if (attributes != null) { for (Project.ExtendedAttributes.ExtendedAttribute ea : attributes.getExtendedAttribute()) { readFieldAlias(ea); } } } /** * Read a single field alias from an extended attribute. * * @param attribute extended attribute */ private void readFieldAlias(Project.ExtendedAttributes.ExtendedAttribute attribute) { String alias = attribute.getAlias(); if (alias != null && alias.length() != 0) { FieldType field = FieldTypeHelper.getInstance(Integer.parseInt(attribute.getFieldID())); m_projectFile.getCustomFields().getCustomField(field).setAlias(attribute.getAlias()); } } /** * This method extracts resource data from an MSPDI file. * * @param project Root node of the MSPDI file * @param calendarMap Map of calendar UIDs to names */ protected void readResources(Project project, HashMap<BigInteger, ProjectCalendar> calendarMap) //claur changed to protected { Project.Resources resources = project.getResources(); if (resources != null) { for (Project.Resources.Resource resource : resources.getResource()) { readResource(resource, calendarMap); } } } /** * This method extracts data for a single resource from an MSPDI file. * * @param xml Resource data * @param calendarMap Map of calendar UIDs to names */ private void readResource(Project.Resources.Resource xml, HashMap<BigInteger, ProjectCalendar> calendarMap) { Resource mpx = m_projectFile.addResource(); mpx.setAccrueAt(xml.getAccrueAt()); mpx.setActveDirectoryGUID(xml.getActiveDirectoryGUID()); mpx.setActualCost(DatatypeConverter.parseCurrency(xml.getActualCost())); mpx.setActualOvertimeCost(DatatypeConverter.parseCurrency(xml.getActualOvertimeCost())); mpx.setActualOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getActualOvertimeWork())); mpx.setActualOvertimeWorkProtected(DatatypeConverter.parseDuration(m_projectFile, null, xml.getActualOvertimeWorkProtected())); mpx.setActualWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getActualWork())); mpx.setActualWorkProtected(DatatypeConverter.parseDuration(m_projectFile, null, xml.getActualWorkProtected())); mpx.setACWP(DatatypeConverter.parseCurrency(xml.getACWP())); mpx.setAvailableFrom(DatatypeConverter.parseDate(xml.getAvailableFrom())); mpx.setAvailableTo(DatatypeConverter.parseDate(xml.getAvailableTo())); mpx.setBCWS(DatatypeConverter.parseCurrency(xml.getBCWS())); mpx.setBCWP(DatatypeConverter.parseCurrency(xml.getBCWP())); mpx.setBookingType(xml.getBookingType()); //mpx.setBaseCalendar (); //mpx.setBaselineCost(); //mpx.setBaselineWork(); mpx.setBudget(BooleanHelper.getBoolean(xml.isIsBudget())); mpx.setCanLevel(BooleanHelper.getBoolean(xml.isCanLevel())); mpx.setCode(xml.getCode()); mpx.setCost(DatatypeConverter.parseCurrency(xml.getCost())); mpx.setCostPerUse(DatatypeConverter.parseCurrency(xml.getCostPerUse())); mpx.setCostVariance(DatatypeConverter.parseCurrency(xml.getCostVariance())); mpx.setCreationDate(DatatypeConverter.parseDate(xml.getCreationDate())); mpx.setCV(DatatypeConverter.parseCurrency(xml.getCV())); mpx.setEmailAddress(xml.getEmailAddress()); mpx.setGroup(xml.getGroup()); mpx.setHyperlink(xml.getHyperlink()); mpx.setHyperlinkAddress(xml.getHyperlinkAddress()); mpx.setHyperlinkSubAddress(xml.getHyperlinkSubAddress()); mpx.setID(NumberHelper.getInteger(xml.getID())); mpx.setInitials(xml.getInitials()); mpx.setIsEnterprise(BooleanHelper.getBoolean(xml.isIsEnterprise())); mpx.setIsGeneric(BooleanHelper.getBoolean(xml.isIsGeneric())); mpx.setIsInactive(BooleanHelper.getBoolean(xml.isIsInactive())); mpx.setIsNull(BooleanHelper.getBoolean(xml.isIsNull())); //mpx.setLinkedFields(); mpx.setMaterialLabel(xml.getMaterialLabel()); mpx.setMaxUnits(DatatypeConverter.parseUnits(xml.getMaxUnits())); mpx.setName(xml.getName()); if (xml.getNotes() != null && xml.getNotes().length() != 0) { mpx.setNotes(xml.getNotes()); } mpx.setNtAccount(xml.getNTAccount()); //mpx.setObjects(); mpx.setOvertimeCost(DatatypeConverter.parseCurrency(xml.getOvertimeCost())); mpx.setOvertimeRate(DatatypeConverter.parseRate(xml.getOvertimeRate())); mpx.setOvertimeRateUnits(DatatypeConverter.parseTimeUnit(xml.getOvertimeRateFormat())); mpx.setOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getOvertimeWork())); mpx.setPeakUnits(DatatypeConverter.parseUnits(xml.getPeakUnits())); mpx.setPercentWorkComplete(xml.getPercentWorkComplete()); mpx.setPhonetics(xml.getPhonetics()); mpx.setRegularWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getRegularWork())); mpx.setRemainingCost(DatatypeConverter.parseCurrency(xml.getRemainingCost())); mpx.setRemainingOvertimeCost(DatatypeConverter.parseCurrency(xml.getRemainingOvertimeCost())); mpx.setRemainingWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getRemainingWork())); mpx.setRemainingOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getRemainingOvertimeWork())); mpx.setStandardRate(DatatypeConverter.parseRate(xml.getStandardRate())); mpx.setStandardRateUnits(DatatypeConverter.parseTimeUnit(xml.getStandardRateFormat())); mpx.setSV(DatatypeConverter.parseCurrency(xml.getSV())); mpx.setType(xml.getType()); mpx.setUniqueID(NumberHelper.getInteger(xml.getUID())); mpx.setWork(DatatypeConverter.parseDuration(m_projectFile, null, xml.getWork())); mpx.setWorkGroup(xml.getWorkGroup()); mpx.setWorkVariance(DatatypeConverter.parseDurationInThousanthsOfMinutes(xml.getWorkVariance())); if (mpx.getType() == ResourceType.MATERIAL && BooleanHelper.getBoolean(xml.isIsCostResource())) { mpx.setType(ResourceType.COST); } readResourceExtendedAttributes(xml, mpx); readResourceBaselines(xml, mpx); mpx.setResourceCalendar(calendarMap.get(xml.getCalendarUID())); // ensure that we cache this value mpx.setOverAllocated(BooleanHelper.getBoolean(xml.isOverAllocated())); readCostRateTables(mpx, xml.getRates()); readAvailabilityTable(mpx, xml.getAvailabilityPeriods()); m_eventManager.fireResourceReadEvent(mpx); } /** * Reads baseline values for the current resource. * * @param xmlResource MSPDI resource instance * @param mpxjResource MPXJ resource instance */ private void readResourceBaselines(Project.Resources.Resource xmlResource, Resource mpxjResource) { for (Project.Resources.Resource.Baseline baseline : xmlResource.getBaseline()) { int number = NumberHelper.getInt(baseline.getNumber()); Double cost = DatatypeConverter.parseCurrency(baseline.getCost()); Duration work = DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, baseline.getWork()); if (number == 0) { mpxjResource.setBaselineCost(cost); mpxjResource.setBaselineWork(work); } else { mpxjResource.setBaselineCost(number, cost); mpxjResource.setBaselineWork(number, work); } } } /** * This method processes any extended attributes associated with a resource. * * @param xml MSPDI resource instance * @param mpx MPX resource instance */ private void readResourceExtendedAttributes(Project.Resources.Resource xml, Resource mpx) { for (Project.Resources.Resource.ExtendedAttribute attrib : xml.getExtendedAttribute()) { int xmlFieldID = Integer.parseInt(attrib.getFieldID()) & 0x0000FFFF; ResourceField mpxFieldID = MPPResourceField.getInstance(xmlFieldID); TimeUnit durationFormat = DatatypeConverter.parseDurationTimeUnits(attrib.getDurationFormat(), null); DatatypeConverter.parseExtendedAttribute(m_projectFile, mpx, attrib.getValue(), mpxFieldID, durationFormat); } } /** * Reads the cost rate tables from the file. * * @param resource parent resource * @param rates XML cot rate tables */ private void readCostRateTables(Resource resource, Rates rates) { if (rates == null) { CostRateTable table = new CostRateTable(); table.add(CostRateTableEntry.DEFAULT_ENTRY); resource.setCostRateTable(0, table); table = new CostRateTable(); table.add(CostRateTableEntry.DEFAULT_ENTRY); resource.setCostRateTable(1, table); table = new CostRateTable(); table.add(CostRateTableEntry.DEFAULT_ENTRY); resource.setCostRateTable(2, table); table = new CostRateTable(); table.add(CostRateTableEntry.DEFAULT_ENTRY); resource.setCostRateTable(3, table); table = new CostRateTable(); table.add(CostRateTableEntry.DEFAULT_ENTRY); resource.setCostRateTable(4, table); } else { Set<CostRateTable> tables = new HashSet<CostRateTable>(); for (net.sf.mpxj.mspdi.schema.Project.Resources.Resource.Rates.Rate rate : rates.getRate()) { Rate standardRate = DatatypeConverter.parseRate(rate.getStandardRate()); TimeUnit standardRateFormat = DatatypeConverter.parseTimeUnit(rate.getStandardRateFormat()); Rate overtimeRate = DatatypeConverter.parseRate(rate.getOvertimeRate()); TimeUnit overtimeRateFormat = DatatypeConverter.parseTimeUnit(rate.getOvertimeRateFormat()); Double costPerUse = DatatypeConverter.parseCurrency(rate.getCostPerUse()); Date endDate = DatatypeConverter.parseDate(rate.getRatesTo()); CostRateTableEntry entry = new CostRateTableEntry(standardRate, standardRateFormat, overtimeRate, overtimeRateFormat, costPerUse, endDate); int tableIndex = rate.getRateTable().intValue(); CostRateTable table = resource.getCostRateTable(tableIndex); if (table == null) { table = new CostRateTable(); resource.setCostRateTable(tableIndex, table); } table.add(entry); tables.add(table); } for (CostRateTable table : tables) { Collections.sort(table); } } } /** * Reads the availability table from the file. * * @param resource MPXJ resource instance * @param periods MSPDI availability periods */ private void readAvailabilityTable(Resource resource, AvailabilityPeriods periods) { if (periods != null) { AvailabilityTable table = resource.getAvailability(); List<AvailabilityPeriod> list = periods.getAvailabilityPeriod(); for (AvailabilityPeriod period : list) { Date start = DatatypeConverter.parseDate(period.getAvailableFrom()); Date end = DatatypeConverter.parseDate(period.getAvailableTo()); Number units = DatatypeConverter.parseUnits(period.getAvailableUnits()); Availability availability = new Availability(start, end, units); table.add(availability); } Collections.sort(table); } } /** * This method extracts task data from an MSPDI file. * * @param project Root node of the MSPDI file */ private void readTasks(Project project) { Project.Tasks tasks = project.getTasks(); if (tasks != null) { for (Project.Tasks.Task task : tasks.getTask()) { readTask(task); } for (Project.Tasks.Task task : tasks.getTask()) { readPredecessors(task); } } m_projectFile.updateStructure(); } /** * This method extracts data for a single task from an MSPDI file. * * @param xml Task data */ private void readTask(Project.Tasks.Task xml) { Task mpx = m_projectFile.addTask(); mpx.setNull(BooleanHelper.getBoolean(xml.isIsNull())); mpx.setID(NumberHelper.getInteger(xml.getID())); mpx.setUniqueID(NumberHelper.getInteger(xml.getUID())); if (!mpx.getNull()) { // // Set the duration format up front as this is required later // TimeUnit durationFormat = DatatypeConverter.parseDurationTimeUnits(xml.getDurationFormat()); mpx.setActive(xml.isActive() == null ? true : BooleanHelper.getBoolean(xml.isActive())); mpx.setActualCost(DatatypeConverter.parseCurrency(xml.getActualCost())); mpx.setActualDuration(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getActualDuration())); mpx.setActualFinish(DatatypeConverter.parseDate(xml.getActualFinish())); mpx.setActualOvertimeCost(DatatypeConverter.parseCurrency(xml.getActualOvertimeCost())); mpx.setActualOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getActualOvertimeWork())); mpx.setActualOvertimeWorkProtected(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getActualOvertimeWorkProtected())); mpx.setActualStart(DatatypeConverter.parseDate(xml.getActualStart())); mpx.setActualWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getActualWork())); mpx.setActualWorkProtected(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getActualWorkProtected())); mpx.setACWP(DatatypeConverter.parseCurrency(xml.getACWP())); //mpx.setBaselineCost(); //mpx.setBaselineDuration(); //mpx.setBaselineFinish(); //mpx.setBaselineStart(); //mpx.setBaselineWork(); //mpx.setBCWP(); //mpx.setBCWS(); mpx.setCalendar(getTaskCalendar(xml)); //mpx.setConfirmed(); mpx.setConstraintDate(DatatypeConverter.parseDate(xml.getConstraintDate())); mpx.setConstraintType(DatatypeConverter.parseConstraintType(xml.getConstraintType())); mpx.setContact(xml.getContact()); mpx.setCost(DatatypeConverter.parseCurrency(xml.getCost())); //mpx.setCost1(); //mpx.setCost2(); //mpx.setCost3(); //mpx.setCostVariance(); mpx.setCreateDate(DatatypeConverter.parseDate(xml.getCreateDate())); mpx.setCV(DatatypeConverter.parseCurrency(xml.getCV())); mpx.setDeadline(DatatypeConverter.parseDate(xml.getDeadline())); //mpx.setDelay(); mpx.setDuration(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getDuration())); mpx.setDurationText(xml.getDurationText()); //mpx.setDuration1(); //mpx.setDuration2(); //mpx.setDuration3(); //mpx.setDurationVariance(); mpx.setEarlyFinish(DatatypeConverter.parseDate(xml.getEarlyFinish())); mpx.setEarlyStart(DatatypeConverter.parseDate(xml.getEarlyStart())); mpx.setEarnedValueMethod(DatatypeConverter.parseEarnedValueMethod(xml.getEarnedValueMethod())); mpx.setEffortDriven(BooleanHelper.getBoolean(xml.isEffortDriven())); mpx.setEstimated(BooleanHelper.getBoolean(xml.isEstimated())); mpx.setExternalTask(BooleanHelper.getBoolean(xml.isExternalTask())); mpx.setProject(xml.getExternalTaskProject()); mpx.setFinish(DatatypeConverter.parseDate(xml.getFinish())); mpx.setFinishText(xml.getFinishText()); //mpx.setFinish1(); //mpx.setFinish2(); //mpx.setFinish3(); //mpx.setFinish4(); //mpx.setFinish5(); mpx.setFinishVariance(DatatypeConverter.parseDurationInThousanthsOfMinutes(xml.getFinishVariance())); //mpx.setFixed(); mpx.setFixedCost(DatatypeConverter.parseCurrency(xml.getFixedCost())); mpx.setFixedCostAccrual(xml.getFixedCostAccrual()); //mpx.setFlag1(); //mpx.setFlag2(); //mpx.setFlag3(); //mpx.setFlag4(); //mpx.setFlag5(); //mpx.setFlag6(); //mpx.setFlag7(); //mpx.setFlag8(); //mpx.setFlag9(); //mpx.setFlag10(); // This is not correct? mpx.setFreeSlack(DatatypeConverter.parseDurationInThousanthsOfMinutes(xml.getFreeSlack())); mpx.setHideBar(BooleanHelper.getBoolean(xml.isHideBar())); mpx.setHyperlink(xml.getHyperlink()); mpx.setHyperlinkAddress(xml.getHyperlinkAddress()); mpx.setHyperlinkSubAddress(xml.getHyperlinkSubAddress()); mpx.setIgnoreResourceCalendar(BooleanHelper.getBoolean(xml.isIgnoreResourceCalendar())); mpx.setLateFinish(DatatypeConverter.parseDate(xml.getLateFinish())); mpx.setLateStart(DatatypeConverter.parseDate(xml.getLateStart())); mpx.setLevelAssignments(BooleanHelper.getBoolean(xml.isLevelAssignments())); mpx.setLevelingCanSplit(BooleanHelper.getBoolean(xml.isLevelingCanSplit())); mpx.setLevelingDelayFormat(DatatypeConverter.parseDurationTimeUnits(xml.getLevelingDelayFormat())); if (xml.getLevelingDelay() != null && mpx.getLevelingDelayFormat() != null) { double duration = xml.getLevelingDelay().doubleValue(); if (duration != 0) { mpx.setLevelingDelay(Duration.convertUnits(duration / 10, TimeUnit.MINUTES, mpx.getLevelingDelayFormat(), m_projectFile.getProjectProperties())); } } //mpx.setLinkedFields(); //mpx.setMarked(); mpx.setMilestone(BooleanHelper.getBoolean(xml.isMilestone())); mpx.setName(xml.getName()); if (xml.getNotes() != null && xml.getNotes().length() != 0) { mpx.setNotes(xml.getNotes()); } //mpx.setNumber1(); //mpx.setNumber2(); //mpx.setNumber3(); //mpx.setNumber4(); //mpx.setNumber5(); //mpx.setObjects(); mpx.setOutlineLevel(NumberHelper.getInteger(xml.getOutlineLevel())); mpx.setOutlineNumber(xml.getOutlineNumber()); mpx.setOverAllocated(BooleanHelper.getBoolean(xml.isOverAllocated())); mpx.setOvertimeCost(DatatypeConverter.parseCurrency(xml.getOvertimeCost())); mpx.setOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getOvertimeWork())); mpx.setPercentageComplete(xml.getPercentComplete()); mpx.setPercentageWorkComplete(xml.getPercentWorkComplete()); mpx.setPhysicalPercentComplete(NumberHelper.getInteger(xml.getPhysicalPercentComplete())); mpx.setPreleveledFinish(DatatypeConverter.parseDate(xml.getPreLeveledFinish())); mpx.setPreleveledStart(DatatypeConverter.parseDate(xml.getPreLeveledStart())); mpx.setPriority(DatatypeConverter.parsePriority(xml.getPriority())); //mpx.setProject(); mpx.setRecurring(BooleanHelper.getBoolean(xml.isRecurring())); mpx.setRegularWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getRegularWork())); mpx.setRemainingCost(DatatypeConverter.parseCurrency(xml.getRemainingCost())); mpx.setRemainingDuration(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getRemainingDuration())); mpx.setRemainingOvertimeCost(DatatypeConverter.parseCurrency(xml.getRemainingOvertimeCost())); mpx.setRemainingOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getRemainingOvertimeWork())); mpx.setRemainingWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getRemainingWork())); //mpx.setResourceGroup(); //mpx.setResourceInitials(); //mpx.setResourceNames(); mpx.setResume(DatatypeConverter.parseDate(xml.getResume())); mpx.setResumeValid(BooleanHelper.getBoolean(xml.isResumeValid())); //mpx.setResumeNoEarlierThan(); mpx.setRollup(BooleanHelper.getBoolean(xml.isRollup())); mpx.setStart(DatatypeConverter.parseDate(xml.getStart())); mpx.setStartText(xml.getStartText()); //mpx.setStart1(); //mpx.setStart2(); //mpx.setStart3(); //mpx.setStart4(); //mpx.setStart5(); mpx.setStartVariance(DatatypeConverter.parseDurationInThousanthsOfMinutes(xml.getStartVariance())); mpx.setStop(DatatypeConverter.parseDate(xml.getStop())); mpx.setSubProject(BooleanHelper.getBoolean(xml.isIsSubproject()) ? new SubProject() : null); mpx.setSubprojectName(xml.getSubprojectName()); mpx.setSubprojectReadOnly(BooleanHelper.getBoolean(xml.isIsSubprojectReadOnly())); //mpx.setSuccessors(); mpx.setSummary(BooleanHelper.getBoolean(xml.isSummary())); //mpx.setSV(); //mpx.setText1(); //mpx.setText2(); //mpx.setText3(); //mpx.setText4(); //mpx.setText5(); //mpx.setText6(); //mpx.setText7(); //mpx.setText8(); //mpx.setText9(); //mpx.setText10(); mpx.setTaskMode(BooleanHelper.getBoolean(xml.isManual()) ? TaskMode.MANUALLY_SCHEDULED : TaskMode.AUTO_SCHEDULED); mpx.setType(xml.getType()); //mpx.setUpdateNeeded(); mpx.setWBS(xml.getWBS()); mpx.setWBSLevel(xml.getWBSLevel()); mpx.setWork(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getWork())); mpx.setWorkVariance(Duration.getInstance(NumberHelper.getDouble(xml.getWorkVariance()) / 1000, TimeUnit.MINUTES)); // read last to ensure correct caching mpx.setTotalSlack(DatatypeConverter.parseDurationInThousanthsOfMinutes(xml.getTotalSlack())); mpx.setCritical(BooleanHelper.getBoolean(xml.isCritical())); readTaskExtendedAttributes(xml, mpx); readTaskBaselines(xml, mpx, durationFormat); if (mpx.getTaskMode() == TaskMode.MANUALLY_SCHEDULED) { mpx.setManualDuration(DatatypeConverter.parseDuration(m_projectFile, durationFormat, xml.getManualDuration())); } } m_eventManager.fireTaskReadEvent(mpx); } /** * Reads baseline values for the current task. * * @param xmlTask MSPDI task instance * @param mpxjTask MPXJ task instance * @param durationFormat duration format to use */ private void readTaskBaselines(Project.Tasks.Task xmlTask, Task mpxjTask, TimeUnit durationFormat) { for (Project.Tasks.Task.Baseline baseline : xmlTask.getBaseline()) { int number = NumberHelper.getInt(baseline.getNumber()); Double cost = DatatypeConverter.parseCurrency(baseline.getCost()); Duration duration = DatatypeConverter.parseDuration(m_projectFile, durationFormat, baseline.getDuration()); Date finish = DatatypeConverter.parseDate(baseline.getFinish()); Date start = DatatypeConverter.parseDate(baseline.getStart()); Duration work = DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, baseline.getWork()); if (number == 0) { mpxjTask.setBaselineCost(cost); mpxjTask.setBaselineDuration(duration); mpxjTask.setBaselineFinish(finish); mpxjTask.setBaselineStart(start); mpxjTask.setBaselineWork(work); } else { mpxjTask.setBaselineCost(number, cost); mpxjTask.setBaselineDuration(number, duration); mpxjTask.setBaselineFinish(number, finish); mpxjTask.setBaselineStart(number, start); mpxjTask.setBaselineWork(number, work); } } } /** * This method processes any extended attributes associated with a task. * * @param xml MSPDI task instance * @param mpx MPX task instance */ private void readTaskExtendedAttributes(Project.Tasks.Task xml, Task mpx) { for (Project.Tasks.Task.ExtendedAttribute attrib : xml.getExtendedAttribute()) { int xmlFieldID = Integer.parseInt(attrib.getFieldID()) & 0x0000FFFF; TaskField mpxFieldID = MPPTaskField.getInstance(xmlFieldID); TimeUnit durationFormat = DatatypeConverter.parseDurationTimeUnits(attrib.getDurationFormat(), null); DatatypeConverter.parseExtendedAttribute(m_projectFile, mpx, attrib.getValue(), mpxFieldID, durationFormat); } } /** * This method is used to retrieve the calendar associated * with a task. If no calendar is associated with a task, this method * returns null. * * @param task MSPDI task * @return calendar instance */ private ProjectCalendar getTaskCalendar(Project.Tasks.Task task) { ProjectCalendar calendar = null; BigInteger calendarID = task.getCalendarUID(); if (calendarID != null) { calendar = m_projectFile.getCalendarByUniqueID(Integer.valueOf(calendarID.intValue())); } return (calendar); } /** * This method extracts predecessor data from an MSPDI file. * * @param task Task data */ protected void readPredecessors(Project.Tasks.Task task) //claur changed to protected { Integer uid = task.getUID(); if (uid != null) { Task currTask = m_projectFile.getTaskByUniqueID(uid); if (currTask != null) { for (Project.Tasks.Task.PredecessorLink link : task.getPredecessorLink()) { readPredecessor(currTask, link); } } } } /** * This method extracts data for a single predecessor from an MSPDI file. * * @param currTask Current task object * @param link Predecessor data */ protected void readPredecessor(Task currTask, Project.Tasks.Task.PredecessorLink link) //claur changed to protected { BigInteger uid = link.getPredecessorUID(); if (uid != null) { Task prevTask = m_projectFile.getTaskByUniqueID(Integer.valueOf(uid.intValue())); if (prevTask != null) { RelationType type; if (link.getType() != null) { type = RelationType.getInstance(link.getType().intValue()); } else { type = RelationType.FINISH_START; } int lag; if (link.getLinkLag() != null) { lag = link.getLinkLag().intValue() / 10; } else { lag = 0; } TimeUnit lagUnits = DatatypeConverter.parseDurationTimeUnits(link.getLagFormat()); Duration lagDuration = Duration.convertUnits(lag, TimeUnit.MINUTES, lagUnits, m_projectFile.getProjectProperties()); Relation relation = currTask.addPredecessor(prevTask, type, lagDuration); m_eventManager.fireRelationReadEvent(relation); } } } /** * This method extracts assignment data from an MSPDI file. * * @param project Root node of the MSPDI file */ private void readAssignments(Project project) { Project.Assignments assignments = project.getAssignments(); if (assignments != null) { SplitTaskFactory splitFactory = new SplitTaskFactory(); TimephasedWorkNormaliser normaliser = new MSPDITimephasedWorkNormaliser(); for (Project.Assignments.Assignment assignment : assignments.getAssignment()) { readAssignment(assignment, splitFactory, normaliser); } } } /** * This method extracts data for a single assignment from an MSPDI file. * * @param assignment Assignment data * @param splitFactory split task handling * @param normaliser timephased resource assignment normaliser */ protected ResourceAssignment readAssignment(Project.Assignments.Assignment assignment, SplitTaskFactory splitFactory, TimephasedWorkNormaliser normaliser) {//claur changed to protected, changed return type from void to ResourceAssignment BigInteger taskUID = assignment.getTaskUID(); BigInteger resourceUID = assignment.getResourceUID(); if (taskUID != null && resourceUID != null) { Task task = m_projectFile.getTaskByUniqueID(Integer.valueOf(taskUID.intValue())); Resource resource = m_projectFile.getResourceByUniqueID(Integer.valueOf(resourceUID.intValue())); //System.out.println(task); ProjectCalendar calendar = null; if (resource != null) { calendar = resource.getResourceCalendar(); } if (calendar == null) { calendar = task.getCalendar(); } if (calendar == null) { calendar = m_projectFile.getDefaultCalendar(); } LinkedList<TimephasedWork> timephasedComplete = readTimephasedAssignment(calendar, assignment, 2); LinkedList<TimephasedWork> timephasedPlanned = readTimephasedAssignment(calendar, assignment, 1); boolean raw = true; if (isSplit(calendar, timephasedComplete) || isSplit(calendar, timephasedPlanned)) { task.setSplits(new LinkedList<DateRange>()); normaliser.normalise(calendar, timephasedComplete); normaliser.normalise(calendar, timephasedPlanned); splitFactory.processSplitData(task, timephasedComplete, timephasedPlanned); raw = false; } DefaultTimephasedWorkContainer timephasedCompleteData = new DefaultTimephasedWorkContainer(calendar, normaliser, timephasedComplete, raw); DefaultTimephasedWorkContainer timephasedPlannedData = new DefaultTimephasedWorkContainer(calendar, normaliser, timephasedPlanned, raw); if (task != null) { ResourceAssignment mpx = task.addResourceAssignment(resource); mpx.setActualCost(DatatypeConverter.parseCurrency(assignment.getActualCost())); mpx.setActualFinish(DatatypeConverter.parseDate(assignment.getActualFinish())); mpx.setActualOvertimeCost(DatatypeConverter.parseCurrency(assignment.getActualOvertimeCost())); mpx.setActualOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getActualOvertimeWork())); //assignment.getActualOvertimeWorkProtected() mpx.setActualStart(DatatypeConverter.parseDate(assignment.getActualStart())); mpx.setActualWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getActualWork())); //assignment.getActualWorkProtected() mpx.setACWP(DatatypeConverter.parseCurrency(assignment.getACWP())); mpx.setBCWP(DatatypeConverter.parseCurrency(assignment.getBCWP())); mpx.setBCWS(DatatypeConverter.parseCurrency(assignment.getBCWS())); //assignment.getBookingType() mpx.setBudgetCost(DatatypeConverter.parseCurrency(assignment.getBudgetCost())); mpx.setBudgetWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getBudgetWork())); mpx.setCost(DatatypeConverter.parseCurrency(assignment.getCost())); mpx.setCostRateTableIndex(NumberHelper.getInt(assignment.getCostRateTable())); mpx.setCreateDate(DatatypeConverter.parseDate(assignment.getCreationDate())); mpx.setCV(DatatypeConverter.parseCurrency(assignment.getCV())); mpx.setDelay(DatatypeConverter.parseDurationInTenthsOfMinutes(assignment.getDelay())); mpx.setFinish(DatatypeConverter.parseDate(assignment.getFinish())); mpx.setVariableRateUnits(BooleanHelper.getBoolean(assignment.isHasFixedRateUnits()) ? null : DatatypeConverter.parseTimeUnit(assignment.getRateScale())); mpx.setHyperlink(assignment.getHyperlink()); mpx.setHyperlinkAddress(assignment.getHyperlinkAddress()); mpx.setHyperlinkSubAddress(assignment.getHyperlinkSubAddress()); mpx.setLevelingDelay(DatatypeConverter.parseDurationInTenthsOfMinutes(m_projectFile.getProjectProperties(), assignment.getLevelingDelay(), DatatypeConverter.parseDurationTimeUnits(assignment.getLevelingDelayFormat()))); mpx.setNotes(assignment.getNotes()); mpx.setOvertimeCost(DatatypeConverter.parseCurrency(assignment.getOvertimeCost())); mpx.setOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getOvertimeWork())); mpx.setPercentageWorkComplete(assignment.getPercentWorkComplete()); //mpx.setPlannedCost(); //mpx.setPlannedWork(); mpx.setRegularWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getRegularWork())); mpx.setRemainingCost(DatatypeConverter.parseCurrency(assignment.getRemainingCost())); mpx.setRemainingOvertimeCost(DatatypeConverter.parseCurrency(assignment.getRemainingOvertimeCost())); mpx.setRemainingOvertimeWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getRemainingOvertimeWork())); mpx.setRemainingWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getRemainingWork())); //assignment.getResume() mpx.setStart(DatatypeConverter.parseDate(assignment.getStart())); //assignment.getStop() mpx.setSV(DatatypeConverter.parseCurrency(assignment.getSV())); mpx.setUniqueID(NumberHelper.getInteger(assignment.getUID())); mpx.setUnits(DatatypeConverter.parseUnits(assignment.getUnits())); mpx.setVAC(DatatypeConverter.parseCurrency(assignment.getVAC())); mpx.setWork(DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, assignment.getWork())); mpx.setWorkContour(assignment.getWorkContour()); mpx.setTimephasedActualWork(timephasedCompleteData); mpx.setTimephasedWork(timephasedPlannedData); readAssignmentExtendedAttributes(assignment, mpx); readAssignmentBaselines(assignment, mpx); // Read last to ensure caching works as expected mpx.setCostVariance(DatatypeConverter.parseCurrency(assignment.getCostVariance())); mpx.setWorkVariance(DatatypeConverter.parseDurationInThousanthsOfMinutes(m_projectFile.getProjectProperties(), assignment.getWorkVariance(), TimeUnit.HOURS)); mpx.setStartVariance(DatatypeConverter.parseDurationInTenthsOfMinutes(m_projectFile.getProjectProperties(), assignment.getStartVariance(), TimeUnit.DAYS)); mpx.setFinishVariance(DatatypeConverter.parseDurationInTenthsOfMinutes(m_projectFile.getProjectProperties(), assignment.getFinishVariance(), TimeUnit.DAYS)); m_eventManager.fireAssignmentReadEvent(mpx); return mpx; //claur } } return null; //claur } /** * Extracts assignment baseline data. * * @param assignment xml assignment * @param mpx mpxj assignment */ private void readAssignmentBaselines(Project.Assignments.Assignment assignment, ResourceAssignment mpx) { for (Project.Assignments.Assignment.Baseline baseline : assignment.getBaseline()) { int number = NumberHelper.getInt(baseline.getNumber()); //baseline.getBCWP() //baseline.getBCWS() Number cost = DatatypeConverter.parseExtendedAttributeCurrency(baseline.getCost()); Date finish = DatatypeConverter.parseExtendedAttributeDate(baseline.getFinish()); //baseline.getNumber() Date start = DatatypeConverter.parseExtendedAttributeDate(baseline.getStart()); Duration work = DatatypeConverter.parseDuration(m_projectFile, TimeUnit.HOURS, baseline.getWork()); if (number == 0) { mpx.setBaselineCost(cost); mpx.setBaselineFinish(finish); mpx.setBaselineStart(start); mpx.setBaselineWork(work); } else { mpx.setBaselineCost(number, cost); mpx.setBaselineWork(number, work); mpx.setBaselineStart(number, start); mpx.setBaselineFinish(number, finish); } } } /** * This method processes any extended attributes associated with a * resource assignment. * * @param xml MSPDI resource assignment instance * @param mpx MPX task instance */ private void readAssignmentExtendedAttributes(Project.Assignments.Assignment xml, ResourceAssignment mpx) { for (Project.Assignments.Assignment.ExtendedAttribute attrib : xml.getExtendedAttribute()) { int xmlFieldID = Integer.parseInt(attrib.getFieldID()) & 0x0000FFFF; AssignmentField mpxFieldID = MPPAssignmentField.getInstance(xmlFieldID); TimeUnit durationFormat = DatatypeConverter.parseDurationTimeUnits(attrib.getDurationFormat(), null); DatatypeConverter.parseExtendedAttribute(m_projectFile, mpx, attrib.getValue(), mpxFieldID, durationFormat); } } /** * Test to determine if this is a split task. * * @param calendar current calendar * @param list timephased resource assignment list * @return boolean flag */ private boolean isSplit(ProjectCalendar calendar, List<TimephasedWork> list) { boolean result = false; for (TimephasedWork assignment : list) { if (calendar != null && assignment.getTotalAmount().getDuration() == 0) { Duration calendarWork = calendar.getWork(assignment.getStart(), assignment.getFinish(), TimeUnit.MINUTES); if (calendarWork.getDuration() != 0) { result = true; break; } } } return result; } /** * Reads timephased assignment data. * * @param calendar current calendar * @param assignment assignment data * @param type flag indicating if this is planned or complete work * @return list of timephased resource assignment instances */ private LinkedList<TimephasedWork> readTimephasedAssignment(ProjectCalendar calendar, Project.Assignments.Assignment assignment, int type) { LinkedList<TimephasedWork> result = new LinkedList<TimephasedWork>(); for (TimephasedDataType item : assignment.getTimephasedData()) { if (NumberHelper.getInt(item.getType()) != type) { continue; } Date startDate = DatatypeConverter.parseDate(item.getStart()); Date finishDate = DatatypeConverter.parseDate(item.getFinish()); Duration work = DatatypeConverter.parseDuration(m_projectFile, TimeUnit.MINUTES, item.getValue()); if (work == null) { work = Duration.getInstance(0, TimeUnit.MINUTES); } else { work = Duration.getInstance(NumberHelper.truncate(work.getDuration(), 2), TimeUnit.MINUTES); } TimephasedWork tra = new TimephasedWork(); tra.setStart(startDate); tra.setFinish(finishDate); tra.setTotalAmount(work); result.add(tra); } return result; } /** * Sets a flag indicating that this class will attempt to correct * and read XML which is not compliant with the XML Schema. This * behaviour matches that of Microsoft Project when reading the * same data. * * @param flag input compatibility flag */ public void setMicrosoftProjectCompatibleInput(boolean flag) { m_compatibleInput = flag; } /** * Retrieves a flag indicating that this class will attempt to correct * and read XML which is not compliant with the XML Schema. This * behaviour matches that of Microsoft Project when reading the * same data. * * @return Boolean flag */ public boolean getMicrosoftProjectCompatibleInput() { return (m_compatibleInput); } /** * 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", MSPDIReader.class.getClassLoader()); } catch (JAXBException ex) { CONTEXT_EXCEPTION = ex; CONTEXT = null; } } private boolean m_compatibleInput = true; protected ProjectFile m_projectFile; //claur need protected to add default assignment private EventManager m_eventManager; private List<ProjectListener> m_projectListeners; }