/* * file: PlannerReader.java * author: Jon Iles * copyright: (c) Packwood Software 2007 * date: 22 February 2007 */ /* * 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.planner; import java.io.InputStream; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; 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.ConstraintType; 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.MPXJException; import net.sf.mpxj.Priority; import net.sf.mpxj.ProjectCalendar; import net.sf.mpxj.ProjectCalendarException; import net.sf.mpxj.ProjectCalendarHours; 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.ResourceType; import net.sf.mpxj.Task; import net.sf.mpxj.TaskType; import net.sf.mpxj.TimeUnit; import net.sf.mpxj.common.NumberHelper; import net.sf.mpxj.listener.ProjectListener; import net.sf.mpxj.planner.schema.Allocation; import net.sf.mpxj.planner.schema.Allocations; import net.sf.mpxj.planner.schema.Calendars; import net.sf.mpxj.planner.schema.Constraint; import net.sf.mpxj.planner.schema.Days; import net.sf.mpxj.planner.schema.DefaultWeek; import net.sf.mpxj.planner.schema.Interval; import net.sf.mpxj.planner.schema.OverriddenDayType; import net.sf.mpxj.planner.schema.OverriddenDayTypes; import net.sf.mpxj.planner.schema.Predecessor; import net.sf.mpxj.planner.schema.Predecessors; import net.sf.mpxj.planner.schema.Project; import net.sf.mpxj.planner.schema.Resources; import net.sf.mpxj.planner.schema.Tasks; 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 a Planner file. */ public final class PlannerReader extends AbstractProjectReader { /** * {@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.setAutoTaskUniqueID(false); config.setAutoResourceUniqueID(false); config.setAutoOutlineLevel(false); config.setAutoOutlineNumber(false); config.setAutoWBS(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(); Project plannerProject = (Project) unmarshaller.unmarshal(doc); readProjectProperties(plannerProject); readCalendars(plannerProject); readResources(plannerProject); readTasks(plannerProject); readAssignments(plannerProject); // // Ensure that the unique ID counters are correct // config.updateUniqueCounters(); 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; m_defaultCalendar = null; } } /** * This method extracts project properties from a Planner file. * * @param project Root node of the Planner file */ private void readProjectProperties(Project project) throws MPXJException { ProjectProperties properties = m_projectFile.getProjectProperties(); properties.setCompany(project.getCompany()); properties.setManager(project.getManager()); properties.setName(project.getName()); properties.setStartDate(getDateTime(project.getProjectStart())); } /** * This method extracts calendar data from a Planner file. * * @param project Root node of the Planner file */ private void readCalendars(Project project) throws MPXJException { Calendars calendars = project.getCalendars(); if (calendars != null) { for (net.sf.mpxj.planner.schema.Calendar cal : calendars.getCalendar()) { readCalendar(cal, null); } Integer defaultCalendarID = getInteger(project.getCalendar()); m_defaultCalendar = m_projectFile.getCalendarByUniqueID(defaultCalendarID); if (m_defaultCalendar != null) { m_projectFile.getProjectProperties().setDefaultCalendarName(m_defaultCalendar.getName()); } } } /** * This method extracts data for a single calendar from a Planner file. * * @param plannerCalendar Calendar data * @param parentMpxjCalendar parent of derived calendar */ private void readCalendar(net.sf.mpxj.planner.schema.Calendar plannerCalendar, ProjectCalendar parentMpxjCalendar) throws MPXJException { // // Create a calendar instance // ProjectCalendar mpxjCalendar = m_projectFile.addCalendar(); // // Populate basic details // mpxjCalendar.setUniqueID(getInteger(plannerCalendar.getId())); mpxjCalendar.setName(plannerCalendar.getName()); mpxjCalendar.setParent(parentMpxjCalendar); // // Set working and non working days // DefaultWeek dw = plannerCalendar.getDefaultWeek(); setWorkingDay(mpxjCalendar, Day.MONDAY, dw.getMon()); setWorkingDay(mpxjCalendar, Day.TUESDAY, dw.getTue()); setWorkingDay(mpxjCalendar, Day.WEDNESDAY, dw.getWed()); setWorkingDay(mpxjCalendar, Day.THURSDAY, dw.getThu()); setWorkingDay(mpxjCalendar, Day.FRIDAY, dw.getFri()); setWorkingDay(mpxjCalendar, Day.SATURDAY, dw.getSat()); setWorkingDay(mpxjCalendar, Day.SUNDAY, dw.getSun()); // // Set working hours // processWorkingHours(mpxjCalendar, plannerCalendar); // // Process exception days // processExceptionDays(mpxjCalendar, plannerCalendar); m_eventManager.fireCalendarReadEvent(mpxjCalendar); // // Process any derived calendars // List<net.sf.mpxj.planner.schema.Calendar> calendarList = plannerCalendar.getCalendar(); for (net.sf.mpxj.planner.schema.Calendar cal : calendarList) { readCalendar(cal, mpxjCalendar); } } /** * Set the working/non-working status of a weekday. * * @param mpxjCalendar MPXJ calendar * @param mpxjDay day of the week * @param plannerDay planner day type */ private void setWorkingDay(ProjectCalendar mpxjCalendar, Day mpxjDay, String plannerDay) { DayType dayType = DayType.DEFAULT; if (plannerDay != null) { switch (getInt(plannerDay)) { case 0: { dayType = DayType.WORKING; break; } case 1: { dayType = DayType.NON_WORKING; break; } } } mpxjCalendar.setWorkingDay(mpxjDay, dayType); } /** * Add the appropriate working hours to each working day. * * @param mpxjCalendar MPXJ calendar * @param plannerCalendar Planner calendar */ private void processWorkingHours(ProjectCalendar mpxjCalendar, net.sf.mpxj.planner.schema.Calendar plannerCalendar) throws MPXJException { OverriddenDayTypes types = plannerCalendar.getOverriddenDayTypes(); if (types != null) { List<OverriddenDayType> typeList = types.getOverriddenDayType(); Iterator<OverriddenDayType> iter = typeList.iterator(); OverriddenDayType odt = null; while (iter.hasNext()) { odt = iter.next(); if (getInt(odt.getId()) != 0) { odt = null; continue; } break; } if (odt != null) { List<Interval> intervalList = odt.getInterval(); ProjectCalendarHours mondayHours = null; ProjectCalendarHours tuesdayHours = null; ProjectCalendarHours wednesdayHours = null; ProjectCalendarHours thursdayHours = null; ProjectCalendarHours fridayHours = null; ProjectCalendarHours saturdayHours = null; ProjectCalendarHours sundayHours = null; if (mpxjCalendar.isWorkingDay(Day.MONDAY)) { mondayHours = mpxjCalendar.addCalendarHours(Day.MONDAY); } if (mpxjCalendar.isWorkingDay(Day.TUESDAY)) { tuesdayHours = mpxjCalendar.addCalendarHours(Day.TUESDAY); } if (mpxjCalendar.isWorkingDay(Day.WEDNESDAY)) { wednesdayHours = mpxjCalendar.addCalendarHours(Day.WEDNESDAY); } if (mpxjCalendar.isWorkingDay(Day.THURSDAY)) { thursdayHours = mpxjCalendar.addCalendarHours(Day.THURSDAY); } if (mpxjCalendar.isWorkingDay(Day.FRIDAY)) { fridayHours = mpxjCalendar.addCalendarHours(Day.FRIDAY); } if (mpxjCalendar.isWorkingDay(Day.SATURDAY)) { saturdayHours = mpxjCalendar.addCalendarHours(Day.SATURDAY); } if (mpxjCalendar.isWorkingDay(Day.SUNDAY)) { sundayHours = mpxjCalendar.addCalendarHours(Day.SUNDAY); } for (Interval interval : intervalList) { Date startTime = getTime(interval.getStart()); Date endTime = getTime(interval.getEnd()); m_defaultWorkingHours.add(new DateRange(startTime, endTime)); if (mondayHours != null) { mondayHours.addRange(new DateRange(startTime, endTime)); } if (tuesdayHours != null) { tuesdayHours.addRange(new DateRange(startTime, endTime)); } if (wednesdayHours != null) { wednesdayHours.addRange(new DateRange(startTime, endTime)); } if (thursdayHours != null) { thursdayHours.addRange(new DateRange(startTime, endTime)); } if (fridayHours != null) { fridayHours.addRange(new DateRange(startTime, endTime)); } if (saturdayHours != null) { saturdayHours.addRange(new DateRange(startTime, endTime)); } if (sundayHours != null) { sundayHours.addRange(new DateRange(startTime, endTime)); } } } } } /** * Process exception days. * * @param mpxjCalendar MPXJ calendar * @param plannerCalendar Planner calendar */ private void processExceptionDays(ProjectCalendar mpxjCalendar, net.sf.mpxj.planner.schema.Calendar plannerCalendar) throws MPXJException { Days days = plannerCalendar.getDays(); if (days != null) { List<net.sf.mpxj.planner.schema.Day> dayList = days.getDay(); for (net.sf.mpxj.planner.schema.Day day : dayList) { if (day.getType().equals("day-type")) { Date exceptionDate = getDate(day.getDate()); ProjectCalendarException exception = mpxjCalendar.addCalendarException(exceptionDate, exceptionDate); if (getInt(day.getId()) == 0) { for (int hoursIndex = 0; hoursIndex < m_defaultWorkingHours.size(); hoursIndex++) { DateRange range = m_defaultWorkingHours.get(hoursIndex); exception.addRange(range); } } } } } } /** * This method extracts resource data from a Planner file. * * @param plannerProject Root node of the Planner file */ private void readResources(Project plannerProject) throws MPXJException { Resources resources = plannerProject.getResources(); if (resources != null) { for (net.sf.mpxj.planner.schema.Resource res : resources.getResource()) { readResource(res); } } } /** * This method extracts data for a single resource from a Planner file. * * @param plannerResource Resource data */ private void readResource(net.sf.mpxj.planner.schema.Resource plannerResource) throws MPXJException { Resource mpxjResource = m_projectFile.addResource(); //mpxjResource.setResourceCalendar(m_projectFile.getBaseCalendarByUniqueID(getInteger(plannerResource.getCalendar()))); mpxjResource.setEmailAddress(plannerResource.getEmail()); mpxjResource.setUniqueID(getInteger(plannerResource.getId())); mpxjResource.setName(plannerResource.getName()); mpxjResource.setNotes(plannerResource.getNote()); mpxjResource.setInitials(plannerResource.getShortName()); mpxjResource.setType(getInt(plannerResource.getType()) == 2 ? ResourceType.MATERIAL : ResourceType.WORK); //plannerResource.getStdRate(); //plannerResource.getOvtRate(); //plannerResource.getUnits(); //plannerResource.getProperties(); ProjectCalendar calendar = mpxjResource.addResourceCalendar(); calendar.setWorkingDay(Day.SUNDAY, DayType.DEFAULT); calendar.setWorkingDay(Day.MONDAY, DayType.DEFAULT); calendar.setWorkingDay(Day.TUESDAY, DayType.DEFAULT); calendar.setWorkingDay(Day.WEDNESDAY, DayType.DEFAULT); calendar.setWorkingDay(Day.THURSDAY, DayType.DEFAULT); calendar.setWorkingDay(Day.FRIDAY, DayType.DEFAULT); calendar.setWorkingDay(Day.SATURDAY, DayType.DEFAULT); ProjectCalendar baseCalendar = m_projectFile.getCalendarByUniqueID(getInteger(plannerResource.getCalendar())); if (baseCalendar == null) { baseCalendar = m_defaultCalendar; } calendar.setParent(baseCalendar); m_eventManager.fireResourceReadEvent(mpxjResource); } /** * This method extracts task data from a Planner file. * * @param plannerProject Root node of the Planner file */ private void readTasks(Project plannerProject) throws MPXJException { Tasks tasks = plannerProject.getTasks(); if (tasks != null) { for (net.sf.mpxj.planner.schema.Task task : tasks.getTask()) { readTask(null, task); } for (net.sf.mpxj.planner.schema.Task task : tasks.getTask()) { readPredecessors(task); } } m_projectFile.updateStructure(); } /** * This method extracts data for a single task from a Planner file. * * @param parentTask parent task * @param plannerTask Task data */ private void readTask(Task parentTask, net.sf.mpxj.planner.schema.Task plannerTask) throws MPXJException { Task mpxjTask; if (parentTask == null) { mpxjTask = m_projectFile.addTask(); mpxjTask.setOutlineLevel(Integer.valueOf(1)); } else { mpxjTask = parentTask.addTask(); mpxjTask.setOutlineLevel(Integer.valueOf(parentTask.getOutlineLevel().intValue() + 1)); } // // Read task attributes from Planner // Integer percentComplete = getInteger(plannerTask.getPercentComplete()); //plannerTask.getDuration(); calculate from end - start, not in file? //plannerTask.getEffort(); not set? mpxjTask.setFinish(getDateTime(plannerTask.getEnd())); mpxjTask.setUniqueID(getInteger(plannerTask.getId())); mpxjTask.setName(plannerTask.getName()); mpxjTask.setNotes(plannerTask.getNote()); mpxjTask.setPercentageComplete(percentComplete); mpxjTask.setPercentageWorkComplete(percentComplete); mpxjTask.setPriority(Priority.getInstance(getInt(plannerTask.getPriority()) / 10)); mpxjTask.setType(getTaskType(plannerTask.getScheduling())); //plannerTask.getStart(); // Start day, time is always 00:00? mpxjTask.setMilestone(plannerTask.getType().equals("milestone")); mpxjTask.setWork(getDuration(plannerTask.getWork())); mpxjTask.setStart(getDateTime(plannerTask.getWorkStart())); // // Read constraint // ConstraintType mpxjConstraintType = ConstraintType.AS_SOON_AS_POSSIBLE; Constraint constraint = plannerTask.getConstraint(); if (constraint != null) { if (constraint.getType().equals("start-no-earlier-than")) { mpxjConstraintType = ConstraintType.START_NO_EARLIER_THAN; } else { if (constraint.getType().equals("must-start-on")) { mpxjConstraintType = ConstraintType.MUST_START_ON; } } mpxjTask.setConstraintDate(getDateTime(constraint.getTime())); } mpxjTask.setConstraintType(mpxjConstraintType); // // Calculate missing attributes // ProjectCalendar calendar = m_projectFile.getDefaultCalendar(); if (calendar != null) { Duration duration = calendar.getWork(mpxjTask.getStart(), mpxjTask.getFinish(), TimeUnit.HOURS); double durationDays = duration.getDuration() / 8; if (durationDays > 0) { duration = Duration.getInstance(durationDays, TimeUnit.DAYS); } mpxjTask.setDuration(duration); if (percentComplete.intValue() != 0) { mpxjTask.setActualStart(mpxjTask.getStart()); if (percentComplete.intValue() == 100) { mpxjTask.setActualFinish(mpxjTask.getFinish()); mpxjTask.setActualDuration(duration); mpxjTask.setActualWork(mpxjTask.getWork()); mpxjTask.setRemainingWork(Duration.getInstance(0, TimeUnit.HOURS)); } else { Duration work = mpxjTask.getWork(); Duration actualWork = Duration.getInstance((work.getDuration() * percentComplete.doubleValue()) / 100.0d, work.getUnits()); mpxjTask.setActualDuration(Duration.getInstance((duration.getDuration() * percentComplete.doubleValue()) / 100.0d, duration.getUnits())); mpxjTask.setActualWork(actualWork); mpxjTask.setRemainingWork(Duration.getInstance(work.getDuration() - actualWork.getDuration(), work.getUnits())); } } } mpxjTask.setEffortDriven(true); m_eventManager.fireTaskReadEvent(mpxjTask); // // Process child tasks // List<net.sf.mpxj.planner.schema.Task> childTasks = plannerTask.getTask(); for (net.sf.mpxj.planner.schema.Task childTask : childTasks) { readTask(mpxjTask, childTask); } } /** * This method extracts predecessor data from a Planner file. * * @param plannerTask Task data */ private void readPredecessors(net.sf.mpxj.planner.schema.Task plannerTask) { Task mpxjTask = m_projectFile.getTaskByUniqueID(getInteger(plannerTask.getId())); Predecessors predecessors = plannerTask.getPredecessors(); if (predecessors != null) { List<Predecessor> predecessorList = predecessors.getPredecessor(); for (Predecessor predecessor : predecessorList) { Integer predecessorID = getInteger(predecessor.getPredecessorId()); Task predecessorTask = m_projectFile.getTaskByUniqueID(predecessorID); if (predecessorTask != null) { Duration lag = getDuration(predecessor.getLag()); if (lag == null) { lag = Duration.getInstance(0, TimeUnit.HOURS); } Relation relation = mpxjTask.addPredecessor(predecessorTask, RELATIONSHIP_TYPES.get(predecessor.getType()), lag); m_eventManager.fireRelationReadEvent(relation); } } } // // Process child tasks // List<net.sf.mpxj.planner.schema.Task> childTasks = plannerTask.getTask(); for (net.sf.mpxj.planner.schema.Task childTask : childTasks) { readPredecessors(childTask); } } /** * This method extracts assignment data from a Planner file. * * @param plannerProject Root node of the Planner file */ private void readAssignments(Project plannerProject) { Allocations allocations = plannerProject.getAllocations(); List<Allocation> allocationList = allocations.getAllocation(); Set<Task> tasksWithAssignments = new HashSet<Task>(); for (Allocation allocation : allocationList) { Integer taskID = getInteger(allocation.getTaskId()); Integer resourceID = getInteger(allocation.getResourceId()); Integer units = getInteger(allocation.getUnits()); Task task = m_projectFile.getTaskByUniqueID(taskID); Resource resource = m_projectFile.getResourceByUniqueID(resourceID); if (task != null && resource != null) { Duration work = task.getWork(); int percentComplete = NumberHelper.getInt(task.getPercentageComplete()); ResourceAssignment assignment = task.addResourceAssignment(resource); assignment.setUnits(units); assignment.setWork(work); if (percentComplete != 0) { Duration actualWork = Duration.getInstance((work.getDuration() * percentComplete) / 100, work.getUnits()); assignment.setActualWork(actualWork); assignment.setRemainingWork(Duration.getInstance(work.getDuration() - actualWork.getDuration(), work.getUnits())); } else { assignment.setRemainingWork(work); } assignment.setStart(task.getStart()); assignment.setFinish(task.getFinish()); tasksWithAssignments.add(task); m_eventManager.fireAssignmentReadEvent(assignment); } } // // Adjust work per assignment for tasks with multiple assignments // for (Task task : tasksWithAssignments) { List<ResourceAssignment> assignments = task.getResourceAssignments(); if (assignments.size() > 1) { double maxUnits = 0; for (ResourceAssignment assignment : assignments) { maxUnits += assignment.getUnits().doubleValue(); } for (ResourceAssignment assignment : assignments) { Duration work = assignment.getWork(); double factor = assignment.getUnits().doubleValue() / maxUnits; work = Duration.getInstance(work.getDuration() * factor, work.getUnits()); assignment.setWork(work); Duration actualWork = assignment.getActualWork(); if (actualWork != null) { actualWork = Duration.getInstance(actualWork.getDuration() * factor, actualWork.getUnits()); assignment.setActualWork(actualWork); } Duration remainingWork = assignment.getRemainingWork(); if (remainingWork != null) { remainingWork = Duration.getInstance(remainingWork.getDuration() * factor, remainingWork.getUnits()); assignment.setRemainingWork(remainingWork); } } } } } /** * Convert a Planner date-time value into a Java date. * * 20070222T080000Z * * @param value Planner date-time * @return Java Date instance */ private Date getDateTime(String value) throws MPXJException { try { Number year = m_fourDigitFormat.parse(value.substring(0, 4)); Number month = m_twoDigitFormat.parse(value.substring(4, 6)); Number day = m_twoDigitFormat.parse(value.substring(6, 8)); Number hours = m_twoDigitFormat.parse(value.substring(9, 11)); Number minutes = m_twoDigitFormat.parse(value.substring(11, 13)); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, year.intValue()); cal.set(Calendar.MONTH, month.intValue() - 1); cal.set(Calendar.DAY_OF_MONTH, day.intValue()); cal.set(Calendar.HOUR_OF_DAY, hours.intValue()); cal.set(Calendar.MINUTE, minutes.intValue()); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return (cal.getTime()); } catch (ParseException ex) { throw new MPXJException("Failed to parse date-time " + value, ex); } } /** * Convert a Planner date into a Java date. * * 20070222 * * @param value Planner date * @return Java Date instance */ private Date getDate(String value) throws MPXJException { try { Number year = m_fourDigitFormat.parse(value.substring(0, 4)); Number month = m_twoDigitFormat.parse(value.substring(4, 6)); Number day = m_twoDigitFormat.parse(value.substring(6, 8)); Calendar cal = Calendar.getInstance(); cal.set(Calendar.YEAR, year.intValue()); cal.set(Calendar.MONTH, month.intValue() - 1); cal.set(Calendar.DAY_OF_MONTH, day.intValue()); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return (cal.getTime()); } catch (ParseException ex) { throw new MPXJException("Failed to parse date " + value, ex); } } /** * Convert a Planner time into a Java date. * * 0800 * * @param value Planner time * @return Java Date instance */ private Date getTime(String value) throws MPXJException { try { Number hours = m_twoDigitFormat.parse(value.substring(0, 2)); Number minutes = m_twoDigitFormat.parse(value.substring(2, 4)); Calendar cal = Calendar.getInstance(); cal.set(Calendar.HOUR_OF_DAY, hours.intValue()); cal.set(Calendar.MINUTE, minutes.intValue()); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); return (cal.getTime()); } catch (ParseException ex) { throw new MPXJException("Failed to parse time " + value, ex); } } /** * Convert a string into an Integer. * * @param value integer represented as a string * @return Integer instance */ private Integer getInteger(String value) { return (NumberHelper.getInteger(value)); } /** * Convert a string into an int. * * @param value integer represented as a string * @return int value */ private int getInt(String value) { return (Integer.parseInt(value)); } /** * Convert a string into a long. * * @param value long represented as a string * @return long value */ private long getLong(String value) { return (Long.parseLong(value)); } /** * Convert a string representation of the task type * into a TaskType instance. * * @param value string value * @return TaskType value */ private TaskType getTaskType(String value) { TaskType result = TaskType.FIXED_UNITS; if (value != null && value.equals("fixed-duration")) { result = TaskType.FIXED_DURATION; } return (result); } /** * Converts the string representation of a Planner duration into * an MPXJ Duration instance. * * Planner represents durations as a number of seconds in its * file format, however it displays durations as days and hours, * and seems to assume that a working day is 8 hours. * * @param value string representation of a duration * @return Duration instance */ private Duration getDuration(String value) { Duration result = null; if (value != null && value.length() != 0) { double seconds = getLong(value); double hours = seconds / (60 * 60); double days = hours / 8; if (days < 1) { result = Duration.getInstance(hours, TimeUnit.HOURS); } else { double durationDays = hours / 8; result = Duration.getInstance(durationDays, TimeUnit.DAYS); } } return (result); } private ProjectFile m_projectFile; private EventManager m_eventManager; private ProjectCalendar m_defaultCalendar; private NumberFormat m_twoDigitFormat = new DecimalFormat("00"); private NumberFormat m_fourDigitFormat = new DecimalFormat("0000"); private List<DateRange> m_defaultWorkingHours = new LinkedList<DateRange>(); private List<ProjectListener> m_projectListeners; private static Map<String, RelationType> RELATIONSHIP_TYPES = new HashMap<String, RelationType>(); static { RELATIONSHIP_TYPES.put("FF", RelationType.FINISH_FINISH); RELATIONSHIP_TYPES.put("FS", RelationType.FINISH_START); RELATIONSHIP_TYPES.put("SF", RelationType.START_FINISH); RELATIONSHIP_TYPES.put("SS", RelationType.START_START); } /** * 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.planner.schema", PlannerReader.class.getClassLoader()); } catch (JAXBException ex) { CONTEXT_EXCEPTION = ex; CONTEXT = null; } } }