/* * file: MSPDIFile.java * author: Jon Iles * copyright: (c) Tapster Rock Limited 2002-2003 * date: 20/02/2003 */ /* * 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 com.projity.server.data.mspdi; import java.math.BigInteger; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TimeZone; import javax.xml.bind.JAXBException; import net.sf.mpxj.ProjectCalendar; import net.sf.mpxj.ProjectFile; import net.sf.mpxj.Resource; import net.sf.mpxj.ResourceAssignment; import net.sf.mpxj.Task; import net.sf.mpxj.mspdi.DatatypeConverter; import net.sf.mpxj.mspdi.MSPDIWriter; import net.sf.mpxj.mspdi.schema.Project; import net.sf.mpxj.mspdi.schema.TimephasedDataType; import com.projectlibre.core.time.TimeUtil; import com.projectlibre.core.time.TimephasedType; import com.projectlibre.pm.tasks.SnapshotList; import com.projity.association.AssociationList; import com.projity.configuration.Settings; import com.projity.contrib.util.Log; import com.projity.contrib.util.LogFactory; import com.projity.exchange.ImportedCalendarService; import com.projity.pm.assignment.Assignment; import com.projity.pm.calendar.CalendarService; import com.projity.pm.resource.EnterpriseResource; import com.projity.pm.resource.ResourceImpl; import com.projity.pm.snapshot.Snapshottable; import com.projity.pm.task.NormalTask; import com.projity.pm.task.TaskSnapshot; import com.projity.server.data.MPXConverter; import com.projity.util.DateTime; /** * This class is used to represent a Microsoft Project Data Interchange (MSPDI) * XML file. This implementation allows the file to be read, and the data it * contains exported as a set of MPX objects. These objects can be interrogated * to retrieve any required data, or stored as an MPX file. */ public class ModifiedMSPDIWriter extends MSPDIWriter { //static Log logg=LogFactory.getLog(ModifiedMSPDIFile.class); /** * This constructor allows a new MSPDI file to be created from scratch. */ public ModifiedMSPDIWriter() { super(); } protected Boolean formatOutput() { return Boolean.FALSE; } public void setProjectFile(ProjectFile pf) { m_projectFile = pf; } private Resource unassignedResource() { // add the unassigned resource if (UNASSIGNED == null) { UNASSIGNED = m_projectFile.addResource(); UNASSIGNED.setUniqueID(EnterpriseResource.UNASSIGNED_ID); } return UNASSIGNED; } protected void writeProjectCalendar(Project project) { // projity addition int id = 1; // if not found below, use the default standard calendar ProjectCalendar cal = ImportedCalendarService.getInstance().findExportedCalendar( CalendarService.findBaseCalendar(m_projectFile.getProjectProperties().getDefaultCalendarName())); if (cal != null) { id = cal.getUniqueID(); } else { log.warn("EXPORT: Could not export project calendar: Project: " + m_projectFile.getProjectProperties().getName() + " calendar " + m_projectFile.getProjectProperties().getDefaultCalendarName()); } project.setCalendarUID(BigInteger.valueOf(id)); } /** * This method writes resource data to an MSPDI file. * * @param factory * ObjectFactory instance * @param project * Root node of the MSPDI file * @throws JAXBException * on xml creation errors */ @Override protected void writeResources(Project project){ //claur signature update Project.Resources resources = m_factory.createProjectResources(); //this ok? project.setResources(resources); List<Project.Resources.Resource> list = resources.getResource(); Iterator iter = m_projectFile.getAllResources().iterator(); Resource resource; while (iter.hasNext()) { resource = (Resource) iter.next(); if (resource.getUniqueID() == EnterpriseResource.UNASSIGNED_ID) // don't continue; //DEF167699 projity: Export -> mspdi cannot have ',' or msp complains. // if (Environment.isNoPodServer()) { //claur // String name = resource.getName(); // if (name.contains(",")) { // //Fannon, Tommy -> Tommy Fannon // String fname = name.substring(name.indexOf(',')+2); // //fname = fname.replace(" ", ""); // String lname = name.substring(0, name.indexOf(',')); // name = fname + " " + lname; // //name = name.replace(",", " "); // resource.setName(name); // } // } list.add(writeResource(/*factory,*/ resource)); } } /** * This method writes data for a single task to an MSPDI file. * * @param factory * ObjectFactory instance * @param mpx * Task data * @return new task instance * @throws JAXBException * on xml creation errors */ @Override protected Project.Tasks.Task writeTask(Task mpx) { //signature updated /* DEF167859: Projity: MS Project Export not working mpxj doesn't handle NaN. this would be better fixed in mpxj code itself in the DatatypeConverter, but it would be a pain to setup the development environment to build this... so we will hack this for now --TAF2009-07-29 */ Number fixedCost = mpx.getFixedCost(); if (fixedCost == null || Double.isNaN(fixedCost.doubleValue())) mpx.setFixedCost(null); Project.Tasks.Task xml = super.writeTask(mpx); if (!mpx.getNull()) writeTaskBaselinesAndTimephased(xml, mpx); return xml; } /** * This method writes data for a single assignment to an MSPDI file. * * @param factory * ObjectFactory instance * @param mpx * Resource assignment data * @param uid * Unique ID for the new assignment * @return New MSPDI assignment instance * @throws JAXBException * on xml creation errors */ @Override protected Project.Assignments.Assignment writeAssignment(ResourceAssignment mpx) { Project.Assignments.Assignment xml = super.writeAssignment(mpx); //Microsoft Project does something strange: The unassigned resource has a 0 id for the resource, but assignments use the (short)-1 value. if (mpx.getResourceUniqueID() == 0) xml.setResourceUID(BigInteger.valueOf(EnterpriseResource.UNASSIGNED_ID)); Assignment projityAssignment = (Assignment) projityAssignmentMap.get(mpx); Calendar stop = DateTime.calendarInstance(); stop.setTimeInMillis(projityAssignment.getStop()); xml.setStop(stop); Calendar resume = DateTime.calendarInstance(); resume.setTimeInMillis(projityAssignment.getResume()); xml.setResume(resume); writeAssigmentBaselinesAndTimephased(xml, mpx,projityAssignment.getStart(),projityAssignment.getFinish()); return (xml); } // Projity specific stuff below static Log log = LogFactory.getLog(ModifiedMSPDIWriter.class); protected com.projity.pm.task.Project projityProject; protected Map projityTaskMap = new HashMap(); protected Map projityAssignmentMap = new HashMap(); protected Map projitySnapshotIdMap = new HashMap(); protected Map timephasedMap = new HashMap(); private static Resource UNASSIGNED = null; public void setProjityProject(com.projity.pm.task.Project projityProject) { this.projityProject = projityProject; } public void putProjityTaskMap(Object mpx, Object projity) { projityTaskMap.put(mpx, projity); } public void putProjityAssignmentMap(Object mpx, Object projity) { projityAssignmentMap.put(mpx, projity); } public void putProjitySnapshotIdMap(Object mpx, Object projity) { projitySnapshotIdMap.put(mpx, projity); } public void putTimephasedList(Object mpx, Object timephasedList) { if (mpx == null || timephasedList == null) return; timephasedMap.put(mpx, timephasedList); } public List getTimephasedList(Object mpx) { return (List) timephasedMap.get(mpx); } /** * overloads default behavior to return the "unassigned" resource */ public Resource getResourceByUniqueID(int id) { Resource r; if (id == EnterpriseResource.UNASSIGNED_ID) r = unassignedResource(); else r = m_projectFile.getResourceByUniqueID(id); return r; } private void writeTaskBaselinesAndTimephased(final Project.Tasks.Task xml, Task mpx){ // baselines final List<Project.Tasks.Task.Baseline> baselineList = xml.getBaseline(); NormalTask projityTask = (NormalTask) projityTaskMap.get(mpx); if (projityTask == null) return; for (int s = 0; s < Settings.numBaselines(); s++) { if (s == Snapshottable.CURRENT.intValue()) continue; TaskSnapshot snapshot = (TaskSnapshot) projityTask.getSnapshot(new Integer(s)); if (snapshot == null) continue; Project.Tasks.Task.Baseline baseline = m_factory.createProjectTasksTaskBaseline(); baseline.setNumber(BigInteger.valueOf(s)); Calendar start=Calendar.getInstance(); start.setTimeInMillis(DateTime.fromGmt(projityTask.getBaselineStart(s))); baseline.setStart(start); Calendar finish=Calendar.getInstance(); finish.setTimeInMillis(DateTime.fromGmt(projityTask.getBaselineFinish(s))); baseline.setFinish(finish); baseline.setWork(DatatypeConverter.printDuration(this, MPXConverter.toMPXDuration((long) projityTask.getBaselineWork(s)))); baselineList.add(baseline); // AssociationList snapshotAssignments = snapshot.getHasAssignments().getAssignments(); // if (snapshotAssignments.size() > 0) { // for (Iterator j = snapshotAssignments.iterator(); j.hasNext();) { // Assignment assignment = (Assignment) j.next(); // ResourceImpl r = (ResourceImpl) assignment.getResource(); // if (r.isDefault()) // continue; // // Project.Assignments.Assignment.Baseline baseline = m_factory // .createProjectAssignmentsAssignmentBaseline(); // // For some silly reason, the baseline fields are all // // strings so they need to be converted // // // baseline duration is missing :( // baseline.setNumber(s + ""); // baseline.setStart(MPXConverter.dateToXMLString(DateTime.fromGmt(projityTask.getBaselineStart(s)))); // baseline.setFinish(MPXConverter.dateToXMLString(DateTime.fromGmt(projityTask.getBaselineFinish(s)))); // baseline.setWork(DatatypeConverter.printDuration(this, MPXConverter.toMPXDuration((long) projityTask.getBaselineWork(s)))); // baselineList.add(baseline); // } // } } // There is no need to write out task timephased info since it is all in assignments // final List timephasedList = xml.getTimephasedData(); // TimephasedService.getInstance().consumeTimephased(projityTask, new TimephasedConsumer() { // public void consumeTimephased(Object timephased) { // ((TimephasedDataType) timephased).setUID(xml.getUID()); // timephasedList.add(timephased); // } // }, factory); } private void writeAssigmentBaselinesAndTimephased(final Project.Assignments.Assignment xml, ResourceAssignment mpx, final long assignmentStart, final long assignmentFinish){ //claur signature changed int snapshotId = ((Integer) projitySnapshotIdMap.get(mpx)).intValue(); final Assignment projityAssignment = (Assignment) projityAssignmentMap.get(mpx); // baselines final List<TimephasedDataType> timephasedList = xml.getTimephasedData(); final long[] offset=new long[1]; offset[0]=-1L; final long[] baseLineStart=new long[SnapshotList.BASELINE_COUNT]; final long[] baseLineFinish=new long[SnapshotList.BASELINE_COUNT]; TimephasedService.getInstance().consumeTimephased(projityAssignment, new TimephasedConsumer() { public void consumeTimephased(Object timephased) { TimephasedDataType t=(TimephasedDataType) timephased; if (offset[0]==-1L){ //workaround for scheduling bug //assuming easliest timephased comes first if (t.getStart().getTimeInMillis()<assignmentStart) offset[0]=assignmentStart-t.getStart().getTimeInMillis(); else offset[0]=0; } if (offset[0]>0){ t.getStart().setTimeInMillis(t.getStart().getTimeInMillis()+offset[0]); t.getFinish().setTimeInMillis(t.getFinish().getTimeInMillis()+offset[0]); } //if ("PT0H0M0S".equals(t.getValue())) return; ((TimephasedDataType) timephased).setUID(xml.getUID()); TimephasedType type=TimephasedType.getInstance(t.getType().intValue()); int i=type.getSnapshotId(); if (i>=0 && i<SnapshotList.BASELINE_COUNT){ if(baseLineStart[i]<=0L || baseLineStart[i] < t.getStart().getTimeInMillis()) baseLineStart[i]=t.getStart().getTimeInMillis(); if(baseLineFinish[i]<=0L || baseLineFinish[i] < t.getFinish().getTimeInMillis()) baseLineFinish[i]=t.getFinish().getTimeInMillis(); } timephasedList.add(t); } public boolean acceptValue(double value) { //TODO hack, consumeTimephased shouldn't give PT0H0M0S return value!=0.0; } }, m_factory); for (int i=0; i<SnapshotList.BASELINE_COUNT; i++){ if (baseLineStart[i]>0 && baseLineFinish[i]>0){ Project.Assignments.Assignment.Baseline baseline = m_factory.createProjectAssignmentsAssignmentBaseline(); baseline.setStart(DatatypeConverter.printExtendedAttributeDate(new Date(baseLineStart[i]))); baseline.setFinish(DatatypeConverter.printExtendedAttributeDate(new Date(baseLineFinish[i]))); baseline.setNumber(Integer.toString(i)); xml.getBaseline().add(baseline); } } // for (int s=0; s<SnapshotList.BASELINE_COUNT; s++){ // ResourceImpl r = (ResourceImpl) projityAssignment.getResource(); // if (r.isDefault()) // continue; // // Assignment baselineAssignment=projityAssignment.getBaselineAssignment(s, false); // if (baselineAssignment==null) // continue; // // Project.Assignments.Assignment.Baseline assignmentbaseline = m_factory // .createProjectAssignmentsAssignmentBaseline(); // // For some silly reason, the baseline fields are all // // strings so they need to be converted // // // baseline duration is missing :( // assignmentbaseline.setNumber(s + ""); // assignmentbaseline.setStart(MPXConverter.dateToXMLString(DateTime.fromGmt(baselineAssignment.getStart()))); // assignmentbaseline.setFinish(MPXConverter.dateToXMLString(DateTime.fromGmt(baselineAssignment.getFinish()))); // assignmentbaseline.setWork(DatatypeConverter.printDuration(this, MPXConverter.toMPXDuration((long) baselineAssignment.getWork(null)))); // xml.getBaseline().add(assignmentbaseline); // // // } } public ProjectFile getProjectFile() { return m_projectFile; } }