/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition 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 General
// Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////
package org.projectforge.gantt;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.mpxj.Day;
import net.sf.mpxj.Duration;
import net.sf.mpxj.ProjectCalendar;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.ProjectHeader;
import net.sf.mpxj.RelationType;
import net.sf.mpxj.Task;
import net.sf.mpxj.TimeUnit;
import net.sf.mpxj.mpx.MPXWriter;
import net.sf.mpxj.mspdi.MSPDIWriter;
import net.sf.mpxj.writer.ProjectWriter;
import org.projectforge.calendar.DayHolder;
/**
* Uses the implementation of http://mpxj.sourceforge.net/, which is distributed under the terms of the GNU LGPL.
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class ExportMSProject
{
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(ExportMSProject.class);
public static byte[] exportXml(final GanttChart ganttChart)
{
return export(new MSPDIWriter(), ganttChart);
}
public static byte[] exportMpx(final GanttChart ganttChart)
{
return export(new MPXWriter(), ganttChart);
}
private static byte[] export(final ProjectWriter result, final GanttChart ganttChart)
{
final ProjectFile file = new ProjectFile();
//
// Configure the file to automatically generate identifiers for tasks.
//
file.setAutoTaskID(true);
file.setAutoTaskUniqueID(true);
//
// Configure the file to automatically generate identifiers for resources.
//
file.setAutoResourceID(true);
file.setAutoResourceUniqueID(true);
//
// Configure the file to automatically generate outline levels
// and outline numbers.
//
file.setAutoOutlineLevel(true);
file.setAutoOutlineNumber(true);
//
// Configure the file to automatically generate WBS labels
//
file.setAutoWBS(true);
//
// Configure the file to automatically generate identifiers for calendars
// (not strictly necessary here, but required if generating MSPDI files)
//
file.setAutoCalendarUniqueID(true);
//
// Retrieve the project header and set the start date. Note Microsoft
// Project appears to reset all task dates relative to this date, so this
// date must match the start date of the earliest task for you to see
// the expected results. If this value is not set, it will default to
// today's date.
//
ganttChart.recalculate();
final ProjectHeader header = file.getProjectHeader();
header.setStartDate(ganttChart.getCalculatedStartDate());
//
// Add a default calendar called "Standard"
//
final ProjectCalendar calendar = file.addDefaultBaseCalendar();
calendar.setWorkingDay(Day.SATURDAY, false);
calendar.setWorkingDay(Day.SUNDAY, false);
final DayHolder dh = new DayHolder(ganttChart.getCalculatedStartDate());
for (int i = 0; i < 3000; i++) { // Endless loop protection (paranoia)
dh.add(Calendar.DAY_OF_MONTH, 1);
if (dh.isWorkingDay() == false && dh.isHoliday() == true && dh.isWeekend() == false) {
// Add this holiday to the calendar:
final Date date = dh.getSQLDate();
calendar.addCalendarException(date, date);
if (log.isDebugEnabled() == true) {
log.debug("Add holiday: " + date);
}
}
if (dh.before(ganttChart.getCalculatedEndDate()) == false) {
break;
}
}
final List<GanttTask> children = ganttChart.getRootNode().getChildren();
if (children != null) {
final Map<Serializable, Task> taskMap = new HashMap<Serializable, Task>();
for (final GanttTask child : children) {
addTask(file, taskMap, null, child);
}
for (final GanttTask child : children) {
setPredecessors(taskMap, child);
}
}
//
// Write the file
//
final ByteArrayOutputStream ba = new ByteArrayOutputStream();
try {
result.write(file, ba);
} catch (final IOException ex) {
log.error("Exception encountered " + ex, ex);
}
return ba.toByteArray();
}
private static void addTask(final ProjectFile file, final Map<Serializable, Task> taskMap, final Task parentTask,
final GanttTask ganttTask)
{
final Task task;
if (parentTask == null) {
task = file.addTask();
} else {
task = parentTask.addTask();
}
taskMap.put(ganttTask.getId(), task);
task.setName(ganttTask.getTitle());
if (ganttTask.getStartDate() != null) {
task.setStart(ganttTask.getStartDate());
}
if (ganttTask.getEndDate() != null) {
task.setFinish(ganttTask.getEndDate());
}
final BigDecimal duration = ganttTask.getDuration();
final double value;
if (duration == null) {
value = 0.0;
} else {
value = duration.doubleValue();
}
task.setDuration(Duration.getInstance(value, TimeUnit.DAYS));
if (ganttTask.getProgress() != null) {
task.setPercentageComplete(ganttTask.getProgress());
}
// task2.setActualStart(df.parse("01/01/2003"));
// milestone1.setDuration(Duration.getInstance(0, TimeUnit.DAYS));
final List<GanttTask> children = ganttTask.getChildren();
if (children == null) {
return;
}
for (final GanttTask child : children) {
addTask(file, taskMap, task, child);
}
}
private static void setPredecessors(final Map<Serializable, Task> taskMap, final GanttTask ganttTask)
{
if (ganttTask.getPredecessor() != null) {
final Task task = taskMap.get(ganttTask.getId());
final Task predecessor = taskMap.get(ganttTask.getPredecessorId());
if (task == null) {
log.error("Oups, task with id '" + ganttTask.getId() + "' not found.");
} else if (predecessor == null) {
log.error("Oups, predecessor task with id '" + ganttTask.getPredecessorId() + "' not found.");
} else {
final Integer predecessorOffset = ganttTask.getPredecessorOffset();
final int value;
if (predecessorOffset == null) {
value = 0;
} else {
value = predecessorOffset.intValue();
}
task.addPredecessor(predecessor, getRelationType(ganttTask.getRelationType()), Duration.getInstance(value, TimeUnit.DAYS));
}
}
final List<GanttTask> children = ganttTask.getChildren();
if (children == null) {
return;
}
for (final GanttTask child : children) {
setPredecessors(taskMap, child);
}
}
private static RelationType getRelationType(final GanttRelationType type)
{
if (type == null || type == GanttRelationType.FINISH_START) {
return RelationType.FINISH_START;
} else if (type == GanttRelationType.FINISH_FINISH) {
return RelationType.FINISH_FINISH;
} else if (type == GanttRelationType.START_START) {
return RelationType.START_START;
} else if (type == GanttRelationType.START_FINISH) {
return RelationType.START_FINISH;
} else {
log.error("Unsupported relation type: " + type);
return RelationType.FINISH_START;
}
}
}