/* * file: MPP12Reader.java * author: Jon Iles * copyright: (c) Packwood Software 2002-2005 * date: 05/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.mpp; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import net.sf.mpxj.AssignmentField; import net.sf.mpxj.DateRange; import net.sf.mpxj.Day; import net.sf.mpxj.DayType; import net.sf.mpxj.EventManager; 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.ProjectFile; import net.sf.mpxj.ProjectProperties; import net.sf.mpxj.Resource; import net.sf.mpxj.ResourceField; import net.sf.mpxj.ResourceType; import net.sf.mpxj.SubProject; import net.sf.mpxj.Table; import net.sf.mpxj.TableContainer; import net.sf.mpxj.Task; import net.sf.mpxj.TaskField; import net.sf.mpxj.View; import net.sf.mpxj.WorkGroup; import net.sf.mpxj.common.DateHelper; import net.sf.mpxj.common.NumberHelper; import net.sf.mpxj.common.Pair; import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.DocumentInputStream; /** * This class is used to represent a Microsoft Project MPP12 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. */ final class MPP12Reader implements MPPVariantReader { /** * This method is used to process an MPP12 file. This is the file format * used by Project 12. * * @param reader parent file reader * @param file parent MPP file * @param root Root of the POI file system. */ @Override public void process(MPPReader reader, ProjectFile file, DirectoryEntry root) throws MPXJException, IOException { try { populateMemberData(reader, file, root); processProjectProperties(); if (!reader.getReadPropertiesOnly()) { processSubProjectData(); processGraphicalIndicators(); processCustomValueLists(); processCalendarData(); processResourceData(); processTaskData(); processConstraintData(); processAssignmentData(); postProcessTasks(); if (reader.getReadPresentationData()) { processViewPropertyData(); processTableData(); processViewData(); processFilterData(); processGroupData(); processSavedViewState(); } } } finally { clearMemberData(); } } /** * Populate member data used by the rest of the reader. * * @param reader parent file reader * @param file parent MPP file * @param root Root of the POI file system. */ private void populateMemberData(MPPReader reader, ProjectFile file, DirectoryEntry root) throws MPXJException, IOException { m_reader = reader; m_file = file; m_eventManager = file.getEventManager(); m_root = root; // // Retrieve the high level document properties // Props12 props12 = new Props12(new DocumentInputStream(((DocumentEntry) root.getEntry("Props12")))); //System.out.println(props12); file.getProjectProperties().setProjectFilePath(props12.getUnicodeString(Props.PROJECT_FILE_PATH)); m_inputStreamFactory = new DocumentInputStreamFactory(props12); // // Test for password protection. In the single byte retrieved here: // // 0x00 = no password // 0x01 = protection password has been supplied // 0x02 = write reservation password has been supplied // 0x03 = both passwords have been supplied // if ((props12.getByte(Props.PASSWORD_FLAG) & 0x01) != 0) { // Couldn't figure out how to get the password for MPP12 files so for now we just need to block the reading throw new MPXJException(MPXJException.PASSWORD_PROTECTED); } m_resourceMap = new HashMap<Integer, ProjectCalendar>(); m_projectDir = (DirectoryEntry) root.getEntry(" 112"); m_viewDir = (DirectoryEntry) root.getEntry(" 212"); DirectoryEntry outlineCodeDir = (DirectoryEntry) m_projectDir.getEntry("TBkndOutlCode"); m_outlineCodeVarMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("VarMeta")))); m_outlineCodeVarData = new Var2Data(m_outlineCodeVarMeta, new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("Var2Data")))); m_outlineCodeFixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("FixedMeta"))), 10); m_outlineCodeFixedData = new FixedData(m_outlineCodeFixedMeta, new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("FixedData")))); m_outlineCodeFixedMeta2 = new FixedMeta(new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("Fixed2Meta"))), 10); m_outlineCodeFixedData2 = new FixedData(m_outlineCodeFixedMeta2, new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("Fixed2Data")))); m_projectProps = new Props12(m_inputStreamFactory.getInstance(m_projectDir, "Props")); //MPPUtility.fileDump("c:\\temp\\props.txt", m_projectProps.toString().getBytes()); m_fontBases = new HashMap<Integer, FontBase>(); m_taskSubProjects = new HashMap<Integer, SubProject>(); m_taskOrder = new TreeMap<Long, Integer>(); m_nullTaskOrder = new TreeMap<Integer, Integer>(); m_file.getProjectProperties().setMppFileType(Integer.valueOf(12)); m_file.getProjectProperties().setAutoFilter(props12.getBoolean(Props.AUTO_FILTER)); } /** * Clear transient member data. */ private void clearMemberData() { m_reader = null; m_file = null; m_eventManager = null; m_root = null; m_resourceMap = null; m_projectDir = null; m_viewDir = null; m_outlineCodeVarData = null; m_outlineCodeVarMeta = null; m_projectProps = null; m_fontBases = null; m_taskSubProjects = null; m_taskOrder = null; m_nullTaskOrder = null; m_inputStreamFactory = null; } /** * This method extracts and collates the value list information * for custom column value lists. */ private void processCustomValueLists() throws IOException { DirectoryEntry taskDir = (DirectoryEntry) m_projectDir.getEntry("TBkndTask"); Props taskProps = new Props12(m_inputStreamFactory.getInstance(taskDir, "Props")); CustomFieldValueReader12 reader = new CustomFieldValueReader12(m_file.getProjectProperties(), m_file.getCustomFields(), m_outlineCodeVarMeta, m_outlineCodeVarData, m_outlineCodeFixedData, m_outlineCodeFixedData2, taskProps); reader.process(); } /** * Process the project properties data. */ private void processProjectProperties() throws MPXJException { ProjectPropertiesReader reader = new ProjectPropertiesReader(); reader.process(m_file, m_projectProps, m_root); } /** * Process the graphical indicator data. */ private void processGraphicalIndicators() { GraphicalIndicatorReader graphicalIndicatorReader = new GraphicalIndicatorReader(); graphicalIndicatorReader.process(m_file.getCustomFields(), m_file.getProjectProperties(), m_projectProps); } /** * Read sub project data from the file, and add it to a hash map * indexed by task ID. * * Project stores all subprojects that have ever been inserted into this project * in sequence and that is what used to count unique id offsets for each of the * subprojects. */ private void processSubProjectData() { byte[] subProjData = m_projectProps.getByteArray(Props.SUBPROJECT_DATA); //System.out.println (MPPUtility.hexdump(subProjData, true, 16, "")); //MPPUtility.fileHexDump("c:\\temp\\dump.txt", subProjData); if (subProjData != null) { int index = 0; int offset = 0; int itemHeaderOffset; int uniqueIDOffset; int filePathOffset; int fileNameOffset; byte[] itemHeader = new byte[20]; /*int blockSize = MPPUtility.getInt(subProjData, offset);*/ offset += 4; /*int unknown = MPPUtility.getInt(subProjData, offset);*/ offset += 4; int itemCountOffset = MPPUtility.getInt(subProjData, offset); offset += 4; while (offset < itemCountOffset) { index++; itemHeaderOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; MPPUtility.getByteArray(subProjData, itemHeaderOffset, itemHeader.length, itemHeader, 0); //System.out.println(); //System.out.println (MPPUtility.hexdump(itemHeader, false, 16, "")); //System.out.println(MPPUtility.hexdump(subProjData, offset, 16, false)); //System.out.println("Offset1: " + (MPPUtility.getInt(subProjData, offset) & 0x1FFFF)); //System.out.println("Offset2: " + (MPPUtility.getInt(subProjData, offset+4) & 0x1FFFF)); //System.out.println("Offset3: " + (MPPUtility.getInt(subProjData, offset+8) & 0x1FFFF)); //System.out.println("Offset4: " + (MPPUtility.getInt(subProjData, offset+12) & 0x1FFFF)); //System.out.println ("Offset: " + offset); //System.out.println ("Item Header Offset: " + itemHeaderOffset); byte subProjectType = itemHeader[16]; //System.out.println("SubProjectType: " + Integer.toHexString(subProjectType)); switch (subProjectType) { // // Subproject that is no longer inserted. This is a placeholder in order to be // able to always guarantee unique unique ids. // case 0x00: // // deleted entry? // case 0x10: { offset += 8; break; } // // task unique ID, 8 bytes, path, file name // case 0x0b: case (byte) 0x99: case 0x09: case 0x0D: { uniqueIDOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; // sometimes offset of a task ID? offset += 4; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // // task unique ID, 8 bytes, path, file name // case 0x03: case 0x11: case (byte) 0x91: { uniqueIDOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; // Unknown offset offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // // task unique ID, path, unknown, file name // case (byte) 0x81: case (byte) 0x83: case 0x41: { uniqueIDOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; // unknown offset to 2 bytes of data? offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // // task unique ID, path, file name // case 0x01: case 0x08: { uniqueIDOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // // task unique ID, path, file name // case (byte) 0xC0: { uniqueIDOffset = itemHeaderOffset; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; // unknown offset offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // // resource, task unique ID, path, file name // case 0x05: { uniqueIDOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; m_file.getSubProjects().setResourceSubProject(readSubProject(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index)); break; } case 0x45: { uniqueIDOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; offset += 4; m_file.getSubProjects().setResourceSubProject(readSubProject(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index)); break; } // // path, file name // case 0x02: { //filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; //fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; //sp = readSubProject(subProjData, -1, filePathOffset, fileNameOffset, index); // 0x02 looks to be the link FROM the resource pool to a project that is using it. break; } case 0x04: { filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; m_file.getSubProjects().setResourceSubProject(readSubProject(subProjData, -1, filePathOffset, fileNameOffset, index)); break; } // // task unique ID, 4 bytes, path, 4 bytes, file name // case (byte) 0x8D: { uniqueIDOffset = MPPUtility.getShort(subProjData, offset); offset += 8; filePathOffset = MPPUtility.getShort(subProjData, offset); offset += 8; fileNameOffset = MPPUtility.getShort(subProjData, offset); offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // // task unique ID, path, file name // case 0x0A: { uniqueIDOffset = MPPUtility.getShort(subProjData, offset); offset += 4; filePathOffset = MPPUtility.getShort(subProjData, offset); offset += 4; fileNameOffset = MPPUtility.getShort(subProjData, offset); offset += 4; readSubProjects(subProjData, uniqueIDOffset, filePathOffset, fileNameOffset, index); break; } // new resource pool entry case (byte) 0x44: { filePathOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; offset += 4; fileNameOffset = MPPUtility.getInt(subProjData, offset) & 0x1FFFF; offset += 4; readSubProjects(subProjData, -1, filePathOffset, fileNameOffset, index); break; } // // Appears when a subproject is collapsed // case (byte) 0x80: { offset += 12; break; } // // Any other value, assume 12 bytes to handle old/deleted data? // default: { offset += 12; break; } } } } } /** * Read a list of sub projects. * * @param data byte array * @param uniqueIDOffset offset of unique ID * @param filePathOffset offset of file path * @param fileNameOffset offset of file name * @param subprojectIndex index of the subproject, used to calculate unique id offset */ private void readSubProjects(byte[] data, int uniqueIDOffset, int filePathOffset, int fileNameOffset, int subprojectIndex) { while (uniqueIDOffset < filePathOffset) { readSubProject(data, uniqueIDOffset, filePathOffset, fileNameOffset, subprojectIndex++); uniqueIDOffset += 4; } } /** * Method used to read the sub project details from a byte array. * * @param data byte array * @param uniqueIDOffset offset of unique ID * @param filePathOffset offset of file path * @param fileNameOffset offset of file name * @param subprojectIndex index of the subproject, used to calculate unique id offset * @return new SubProject instance */ private SubProject readSubProject(byte[] data, int uniqueIDOffset, int filePathOffset, int fileNameOffset, int subprojectIndex) { try { SubProject sp = new SubProject(); int type = SUBPROJECT_TASKUNIQUEID0; if (uniqueIDOffset != -1) { int value = MPPUtility.getInt(data, uniqueIDOffset); type = MPPUtility.getInt(data, uniqueIDOffset + 4); switch (type) { case SUBPROJECT_TASKUNIQUEID0: case SUBPROJECT_TASKUNIQUEID1: case SUBPROJECT_TASKUNIQUEID2: case SUBPROJECT_TASKUNIQUEID3: case SUBPROJECT_TASKUNIQUEID4: case SUBPROJECT_TASKUNIQUEID5: case SUBPROJECT_TASKUNIQUEID6: { sp.setTaskUniqueID(Integer.valueOf(value)); m_taskSubProjects.put(sp.getTaskUniqueID(), sp); break; } default: { if (value != 0) { sp.addExternalTaskUniqueID(Integer.valueOf(value)); m_taskSubProjects.put(Integer.valueOf(value), sp); } break; } } // Now get the unique id offset for this subproject value = 0x00800000 + ((subprojectIndex - 1) * 0x00400000); sp.setUniqueIDOffset(Integer.valueOf(value)); } if (type == SUBPROJECT_TASKUNIQUEID4) { sp.setFullPath(MPPUtility.getUnicodeString(data, filePathOffset)); } else { // // First block header // filePathOffset += 18; // // String size as a 4 byte int // filePathOffset += 4; // // Full DOS path // sp.setDosFullPath(MPPUtility.getString(data, filePathOffset)); filePathOffset += (sp.getDosFullPath().length() + 1); // // 24 byte block // filePathOffset += 24; // // 4 byte block size // int size = MPPUtility.getInt(data, filePathOffset); filePathOffset += 4; if (size == 0) { sp.setFullPath(sp.getDosFullPath()); } else { // // 4 byte unicode string size in bytes // size = MPPUtility.getInt(data, filePathOffset); filePathOffset += 4; // // 2 byte data // filePathOffset += 2; // // Unicode string // sp.setFullPath(MPPUtility.getUnicodeString(data, filePathOffset, size)); //filePathOffset += size; } // // Second block header // fileNameOffset += 18; // // String size as a 4 byte int // fileNameOffset += 4; // // DOS file name // sp.setDosFileName(MPPUtility.getString(data, fileNameOffset)); fileNameOffset += (sp.getDosFileName().length() + 1); // // 24 byte block // fileNameOffset += 24; // // 4 byte block size // size = MPPUtility.getInt(data, fileNameOffset); fileNameOffset += 4; if (size == 0) { sp.setFileName(sp.getDosFileName()); } else { // // 4 byte unicode string size in bytes // size = MPPUtility.getInt(data, fileNameOffset); fileNameOffset += 4; // // 2 byte data // fileNameOffset += 2; // // Unicode string // sp.setFileName(MPPUtility.getUnicodeString(data, fileNameOffset, size)); //fileNameOffset += size; } } //System.out.println(sp.toString()); // Add to the list of subprojects m_file.getSubProjects().add(sp); return (sp); } // // Admit defeat at this point - we have probably stumbled // upon a data format we don't understand, so we'll fail // gracefully here. This will now be reported as a missing // sub project error by end users of the library, rather // than as an exception being thrown. // catch (ArrayIndexOutOfBoundsException ex) { return (null); } } /** * This method process the data held in the props file specific to the * visual appearance of the project data. */ private void processViewPropertyData() throws IOException { Props12 props = new Props12(m_inputStreamFactory.getInstance(m_viewDir, "Props")); byte[] data = props.getByteArray(Props.FONT_BASES); if (data != null) { processBaseFonts(data); } ProjectProperties properties = m_file.getProjectProperties(); properties.setShowProjectSummaryTask(props.getBoolean(Props.SHOW_PROJECT_SUMMARY_TASK)); } /** * Create an index of base font numbers and their associated base * font instances. * @param data property data */ private void processBaseFonts(byte[] data) { int offset = 0; int blockCount = MPPUtility.getShort(data, 0); offset += 2; int size; String name; for (int loop = 0; loop < blockCount; loop++) { /*unknownAttribute = MPPUtility.getShort(data, offset);*/ offset += 2; size = MPPUtility.getShort(data, offset); offset += 2; name = MPPUtility.getUnicodeString(data, offset); offset += 64; if (name.length() != 0) { FontBase fontBase = new FontBase(Integer.valueOf(loop), name, size); m_fontBases.put(fontBase.getIndex(), fontBase); } } } /** * This method maps the task unique identifiers to their index number * within the FixedData block. * * @param fieldMap field map * @param taskFixedMeta Fixed meta data for this task * @param taskFixedData Fixed data for this task * @return Mapping between task identifiers and block position */ private TreeMap<Integer, Integer> createTaskMap(FieldMap fieldMap, FixedMeta taskFixedMeta, FixedData taskFixedData) { TreeMap<Integer, Integer> taskMap = new TreeMap<Integer, Integer>(); int uniqueIdOffset = fieldMap.getFixedDataOffset(TaskField.UNIQUE_ID); int itemCount = taskFixedMeta.getAdjustedItemCount(); int uniqueID; Integer key; // // First three items are not tasks, so let's skip them // for (int loop = 3; loop < itemCount; loop++) { byte[] data = taskFixedData.getByteArrayValue(loop); if (data != null) { byte[] metaData = taskFixedMeta.getByteArrayValue(loop); // // Check for the deleted task flag // int flags = MPPUtility.getInt(metaData, 0); if ((flags & 0x02) != 0) { // Project stores the deleted tasks unique id's into the fixed data as well // and at least in one case the deleted task was listed twice in the list // the second time with data with it causing a phantom task to be shown. // See CalendarErrorPhantomTasks.mpp // // So let's add the unique id for the deleted task into the map so we don't // accidentally include the task later. // uniqueID = MPPUtility.getShort(data, TASK_UNIQUE_ID_FIXED_OFFSET); // Only a short stored for deleted tasks? key = Integer.valueOf(uniqueID); if (taskMap.containsKey(key) == false) { taskMap.put(key, null); // use null so we can easily ignore this later } } else { // // Do we have a null task? // if (data.length == NULL_TASK_BLOCK_SIZE) { uniqueID = MPPUtility.getInt(data, TASK_UNIQUE_ID_FIXED_OFFSET); key = Integer.valueOf(uniqueID); if (taskMap.containsKey(key) == false) { taskMap.put(key, Integer.valueOf(loop)); } } else { // // We apply a heuristic here - if we have more than 75% of the data, we assume // the task is valid. // int maxSize = fieldMap.getMaxFixedDataSize(0); if (maxSize == 0 || ((data.length * 100) / maxSize) > 75) { uniqueID = MPPUtility.getInt(data, uniqueIdOffset); key = Integer.valueOf(uniqueID); if (taskMap.containsKey(key) == false) { taskMap.put(key, Integer.valueOf(loop)); } } } } } } return (taskMap); } /** * This method maps the resource unique identifiers to their index number * within the FixedData block. * * @param fieldMap field map * @param rscFixedMeta resource fixed meta data * @param rscFixedData resource fixed data * @return map of resource IDs to resource data */ private TreeMap<Integer, Integer> createResourceMap(FieldMap fieldMap, FixedMeta rscFixedMeta, FixedData rscFixedData) { TreeMap<Integer, Integer> resourceMap = new TreeMap<Integer, Integer>(); int itemCount = rscFixedMeta.getAdjustedItemCount(); for (int loop = 0; loop < itemCount; loop++) { byte[] data = rscFixedData.getByteArrayValue(loop); if (data == null || data.length < fieldMap.getMaxFixedDataSize(0)) { continue; } Integer uniqueID = Integer.valueOf(MPPUtility.getShort(data, 0)); if (resourceMap.containsKey(uniqueID) == false) { resourceMap.put(uniqueID, Integer.valueOf(loop)); } } return (resourceMap); } /** * The format of the calendar data is a 4 byte header followed * by 7x 60 byte blocks, one for each day of the week. Optionally * following this is a set of 64 byte blocks representing exceptions * to the calendar. */ private void processCalendarData() throws IOException { DirectoryEntry calDir = (DirectoryEntry) m_projectDir.getEntry("TBkndCal"); //MPPUtility.fileHexDump("c:\\temp\\varmeta.txt", new DocumentInputStream (((DocumentEntry)calDir.getEntry("VarMeta")))); VarMeta calVarMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) calDir.getEntry("VarMeta")))); Var2Data calVarData = new Var2Data(calVarMeta, new DocumentInputStream((DocumentEntry) calDir.getEntry("Var2Data"))); //System.out.println(calVarMeta); //System.out.println(calVarData); FixedMeta calFixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) calDir.getEntry("FixedMeta"))), 10); FixedData calFixedData = new FixedData(calFixedMeta, m_inputStreamFactory.getInstance(calDir, "FixedData"), 12); //System.out.println (calFixedMeta); //System.out.println (calFixedData); HashMap<Integer, ProjectCalendar> calendarMap = new HashMap<Integer, ProjectCalendar>(); int items = calFixedData.getItemCount(); List<Pair<ProjectCalendar, Integer>> baseCalendars = new LinkedList<Pair<ProjectCalendar, Integer>>(); byte[] defaultCalendarData = m_projectProps.getByteArray(Props.DEFAULT_CALENDAR_HOURS); ProjectCalendar defaultCalendar = new ProjectCalendar(m_file); processCalendarHours(defaultCalendarData, null, defaultCalendar, true); for (int loop = 0; loop < items; loop++) { byte[] fixedData = calFixedData.getByteArrayValue(loop); if (fixedData != null && fixedData.length >= 8) { int offset = 0; // // Bug 890909, here we ensure that we have a complete 12 byte // block before attempting to process the data. // while (offset + 12 <= fixedData.length) { Integer calendarID = Integer.valueOf(MPPUtility.getInt(fixedData, offset + 0)); int baseCalendarID = MPPUtility.getInt(fixedData, offset + 4); if (calendarID.intValue() > 0 && calendarMap.containsKey(calendarID) == false) { byte[] varData = calVarData.getByteArray(calendarID, CALENDAR_DATA); ProjectCalendar cal; if (baseCalendarID == 0 || baseCalendarID == -1 || baseCalendarID == calendarID.intValue()) { if (varData != null || defaultCalendarData != null) { cal = m_file.addCalendar(); if (varData == null) { varData = defaultCalendarData; } } else { cal = m_file.addDefaultBaseCalendar(); } cal.setName(calVarData.getUnicodeString(calendarID, CALENDAR_NAME)); } else { if (varData != null) { cal = m_file.addCalendar(); } else { cal = m_file.addDefaultDerivedCalendar(); } baseCalendars.add(new Pair<ProjectCalendar, Integer>(cal, Integer.valueOf(baseCalendarID))); Integer resourceID = Integer.valueOf(MPPUtility.getInt(fixedData, offset + 8)); m_resourceMap.put(resourceID, cal); } cal.setUniqueID(calendarID); if (varData != null) { processCalendarHours(varData, defaultCalendar, cal, baseCalendarID == -1); processCalendarExceptions(varData, cal); } calendarMap.put(calendarID, cal); m_eventManager.fireCalendarReadEvent(cal); } offset += 12; } } } updateBaseCalendarNames(baseCalendars, calendarMap); } /** * For a given set of calendar data, this method sets the working * day status for each day, and if present, sets the hours for that * day. * * NOTE: MPP12 defines the concept of working weeks. MPXJ does not * currently support this, and thus we only read the working hours * for the default working week. * * @param data calendar data block * @param defaultCalendar calendar to use for default values * @param cal calendar instance * @param isBaseCalendar true if this is a base calendar */ private void processCalendarHours(byte[] data, ProjectCalendar defaultCalendar, ProjectCalendar cal, boolean isBaseCalendar) { // Dump out the calendar related data and fields. //MPPUtility.dataDump(data, true, false, false, false, true, false, true); int offset; ProjectCalendarHours hours; int periodIndex; int index; int defaultFlag; int periodCount; Date start; long duration; Day day; List<DateRange> dateRanges = new ArrayList<DateRange>(5); for (index = 0; index < 7; index++) { offset = (60 * index); defaultFlag = data == null ? 1 : MPPUtility.getShort(data, offset); day = Day.getInstance(index + 1); if (defaultFlag == 1) { if (isBaseCalendar) { if (defaultCalendar == null) { cal.setWorkingDay(day, DEFAULT_WORKING_WEEK[index]); if (cal.isWorkingDay(day)) { hours = cal.addCalendarHours(Day.getInstance(index + 1)); hours.addRange(ProjectCalendarWeek.DEFAULT_WORKING_MORNING); hours.addRange(ProjectCalendarWeek.DEFAULT_WORKING_AFTERNOON); } } else { boolean workingDay = defaultCalendar.isWorkingDay(day); cal.setWorkingDay(day, workingDay); if (workingDay) { hours = cal.addCalendarHours(Day.getInstance(index + 1)); for (DateRange range : defaultCalendar.getHours(day)) { hours.addRange(range); } } } } else { cal.setWorkingDay(day, DayType.DEFAULT); } } else { dateRanges.clear(); periodIndex = 0; periodCount = MPPUtility.getShort(data, offset + 2); while (periodIndex < periodCount) { int startOffset = offset + 8 + (periodIndex * 2); start = MPPUtility.getTime(data, startOffset); int durationOffset = offset + 20 + (periodIndex * 4); duration = MPPUtility.getDuration(data, durationOffset); Date end = new Date(start.getTime() + duration); dateRanges.add(new DateRange(start, end)); ++periodIndex; } if (dateRanges.isEmpty()) { cal.setWorkingDay(day, false); } else { cal.setWorkingDay(day, true); hours = cal.addCalendarHours(Day.getInstance(index + 1)); for (DateRange range : dateRanges) { hours.addRange(range); } } } } } /** * This method extracts any exceptions associated with a calendar. * * @param data calendar data block * @param cal calendar instance */ private void processCalendarExceptions(byte[] data, ProjectCalendar cal) { // // Handle any exceptions // if (data.length > 420) { int offset = 420; // The first 420 is for the working hours data int exceptionCount = MPPUtility.getShort(data, offset); if (exceptionCount != 0) { int index; ProjectCalendarException exception; long duration; int periodCount; Date start; // // Move to the start of the first exception // offset += 4; // // Each exception is a 92 byte block, followed by a // variable length text block // for (index = 0; index < exceptionCount; index++) { Date fromDate = MPPUtility.getDate(data, offset); Date toDate = MPPUtility.getDate(data, offset + 2); exception = cal.addCalendarException(fromDate, toDate); periodCount = MPPUtility.getShort(data, offset + 14); if (periodCount != 0) { for (int exceptionPeriodIndex = 0; exceptionPeriodIndex < periodCount; exceptionPeriodIndex++) { start = MPPUtility.getTime(data, offset + 20 + (exceptionPeriodIndex * 2)); duration = MPPUtility.getDuration(data, offset + 32 + (exceptionPeriodIndex * 4)); exception.addRange(new DateRange(start, new Date(start.getTime() + duration))); } } // // Extract the name length - ensure that it is aligned to a 4 byte boundary // int exceptionNameLength = MPPUtility.getInt(data, offset + 88); if (exceptionNameLength % 4 != 0) { exceptionNameLength = ((exceptionNameLength / 4) + 1) * 4; } //String exceptionName = MPPUtility.getUnicodeString(data, offset+92); offset += (92 + exceptionNameLength); } } } } /** * The way calendars are stored in an MPP12 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 void updateBaseCalendarNames(List<Pair<ProjectCalendar, Integer>> baseCalendars, HashMap<Integer, ProjectCalendar> map) { for (Pair<ProjectCalendar, Integer> pair : baseCalendars) { ProjectCalendar cal = pair.getFirst(); Integer baseCalendarID = pair.getSecond(); ProjectCalendar baseCal = map.get(baseCalendarID); if (baseCal != null && baseCal.getName() != null) { cal.setParent(baseCal); } else { // Remove invalid calendar to avoid serious problems later. m_file.removeCalendar(cal); } } } /** * This method extracts and collates task data. The code below * goes through the modifier methods of the Task class in alphabetical * order extracting the data from the MPP file. Where there is no * mapping (e.g. the field is calculated on the fly, or we can't * find it in the data) the line is commented out. * * The missing boolean attributes are probably represented in the Props * section of the task data, which we have yet to decode. * * @throws java.io.IOException */ private void processTaskData() throws IOException { FieldMap fieldMap = new FieldMap12(m_file.getProjectProperties(), m_file.getCustomFields()); fieldMap.createTaskFieldMap(m_projectProps); FieldMap enterpriseCustomFieldMap = new FieldMap12(m_file.getProjectProperties(), m_file.getCustomFields()); enterpriseCustomFieldMap.createEnterpriseCustomFieldMap(m_projectProps, TaskField.class); DirectoryEntry taskDir = (DirectoryEntry) m_projectDir.getEntry("TBkndTask"); VarMeta taskVarMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) taskDir.getEntry("VarMeta")))); Var2Data taskVarData = new Var2Data(taskVarMeta, new DocumentInputStream(((DocumentEntry) taskDir.getEntry("Var2Data")))); FixedMeta taskFixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) taskDir.getEntry("FixedMeta"))), 47); FixedData taskFixedData = new FixedData(taskFixedMeta, new DocumentInputStream(((DocumentEntry) taskDir.getEntry("FixedData"))), 768, fieldMap.getMaxFixedDataSize(0)); FixedMeta taskFixed2Meta = new FixedMeta(new DocumentInputStream(((DocumentEntry) taskDir.getEntry("Fixed2Meta"))), 86); FixedData taskFixed2Data = new FixedData(taskFixed2Meta, new DocumentInputStream(((DocumentEntry) taskDir.getEntry("Fixed2Data")))); Props12 props = new Props12(m_inputStreamFactory.getInstance(taskDir, "Props")); //System.out.println(taskFixedMeta); //System.out.println(taskFixedData); //System.out.println(taskVarMeta); //System.out.println(taskVarData); //System.out.println(taskFixed2Meta); //System.out.println(m_outlineCodeVarData.getVarMeta()); //System.out.println(m_outlineCodeVarData); //System.out.println(props); // Process aliases new CustomFieldAliasReader(m_file.getCustomFields(), props.getByteArray(TASK_FIELD_NAME_ALIASES)).process(); TreeMap<Integer, Integer> taskMap = createTaskMap(fieldMap, taskFixedMeta, taskFixedData); // The var data may not contain all the tasks as tasks with no var data assigned will // not be saved in there. Most notably these are tasks with no name. So use the task map // which contains all the tasks. Object[] uniqueIdArray = taskMap.keySet().toArray(); //taskVarMeta.getUniqueIdentifierArray(); Integer offset; byte[] data; byte[] metaData; byte[] metaData2; Task task; boolean autoWBS = true; LinkedList<Task> externalTasks = new LinkedList<Task>(); RecurringTaskReader recurringTaskReader = null; String notes; for (int loop = 0; loop < uniqueIdArray.length; loop++) { Integer uniqueID = (Integer) uniqueIdArray[loop]; offset = taskMap.get(uniqueID); if (taskFixedData.isValidOffset(offset) == false) { continue; } data = taskFixedData.getByteArrayValue(offset.intValue()); Integer id = Integer.valueOf(MPPUtility.getInt(data, fieldMap.getFixedDataOffset(TaskField.ID))); if (data.length == NULL_TASK_BLOCK_SIZE) { task = m_file.addTask(); task.setNull(true); task.setUniqueID(Integer.valueOf(MPPUtility.getShort(data, TASK_UNIQUE_ID_FIXED_OFFSET))); task.setID(Integer.valueOf(MPPUtility.getShort(data, TASK_ID_FIXED_OFFSET))); m_nullTaskOrder.put(task.getID(), task.getUniqueID()); continue; } if (data.length < fieldMap.getMaxFixedDataSize(0)) { if (uniqueID.intValue() == 0) { byte[] newData = new byte[fieldMap.getMaxFixedDataSize(0) + 8]; System.arraycopy(data, 0, newData, 0, data.length); data = newData; } else { continue; } } //System.out.println (id+": "+MPPUtility.hexdump(data, false, 16, "")); metaData = taskFixedMeta.getByteArrayValue(offset.intValue()); //System.out.println (MPPUtility.hexdump(data, false, 16, "")); //System.out.println (MPPUtility.hexdump(metaData, false, 16, "")); //MPPUtility.dataDump(data, true, true, true, true, true, true, true); //MPPUtility.dataDump(metaData, true, true, true, true, true, true, true); //MPPUtility.varDataDump(taskVarData, id, true, true, true, true, true, true); metaData2 = taskFixed2Meta.getByteArrayValue(offset.intValue()); byte[] data2 = taskFixed2Data.getByteArrayValue(offset.intValue()); //System.out.println (MPPUtility.hexdump(metaData2, false, 16, "")); //System.out.println (MPPUtility.hexdump(data2, false, 16, "")); byte[] recurringData = taskVarData.getByteArray(uniqueID, fieldMap.getVarDataKey(TaskField.RECURRING_DATA)); Task temp = m_file.getTaskByID(id); if (temp != null) { // Task with this id already exists... determine if this is the 'real' task by seeing // if this task has some var data. This is sort of hokey, but it's the best method i have // been able to see. if (!taskVarMeta.getUniqueIdentifierSet().contains(uniqueID)) { // Sometimes Project contains phantom tasks that coexist on the same id as a valid // task. In this case don't want to include the phantom task. Seems to be a very rare case. continue; } else if (temp.getName() == null) { // Ok, this looks valid. Remove the previous instance since it is most likely not a valid task. // At worst case this removes a task with an empty name. m_file.removeTask(temp); } } task = m_file.addTask(); task.disableEvents(); fieldMap.populateContainer(TaskField.class, task, uniqueID, new byte[][] { data, data2 }, taskVarData); enterpriseCustomFieldMap.populateContainer(TaskField.class, task, uniqueID, null, taskVarData); task.enableEvents(); task.setEffortDriven((metaData[11] & 0x10) != 0); task.setEstimated(getDurationEstimated(MPPUtility.getShort(data, fieldMap.getFixedDataOffset(TaskField.ACTUAL_DURATION_UNITS)))); task.setExpanded(((metaData[12] & 0x02) == 0)); Integer externalTaskID = task.getSubprojectTaskID(); if (externalTaskID != null && externalTaskID.intValue() != 0) { task.setExternalTask(true); externalTasks.add(task); } task.setFlag(1, (metaData[37] & 0x20) != 0); task.setFlag(2, (metaData[37] & 0x40) != 0); task.setFlag(3, (metaData[37] & 0x80) != 0); task.setFlag(4, (metaData[38] & 0x01) != 0); task.setFlag(5, (metaData[38] & 0x02) != 0); task.setFlag(6, (metaData[38] & 0x04) != 0); task.setFlag(7, (metaData[38] & 0x08) != 0); task.setFlag(8, (metaData[38] & 0x10) != 0); task.setFlag(9, (metaData[38] & 0x20) != 0); task.setFlag(10, (metaData[38] & 0x40) != 0); task.setFlag(11, (metaData[38] & 0x80) != 0); task.setFlag(12, (metaData[39] & 0x01) != 0); task.setFlag(13, (metaData[39] & 0x02) != 0); task.setFlag(14, (metaData[39] & 0x04) != 0); task.setFlag(15, (metaData[39] & 0x08) != 0); task.setFlag(16, (metaData[39] & 0x10) != 0); task.setFlag(17, (metaData[39] & 0x20) != 0); task.setFlag(18, (metaData[39] & 0x40) != 0); task.setFlag(19, (metaData[39] & 0x80) != 0); task.setFlag(20, (metaData[40] & 0x01) != 0); task.setHideBar((metaData[10] & 0x80) != 0); processHyperlinkData(task, taskVarData.getByteArray(uniqueID, fieldMap.getVarDataKey(TaskField.HYPERLINK_DATA))); task.setID(id); task.setIgnoreResourceCalendar((metaData[10] & 0x02) != 0); task.setLevelAssignments((metaData[13] & 0x04) != 0); task.setLevelingCanSplit((metaData[13] & 0x02) != 0); task.setMarked((metaData[9] & 0x40) != 0); task.setMilestone((metaData[8] & 0x20) != 0); task.setOutlineCode(1, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE1_INDEX))); task.setOutlineCode(2, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE2_INDEX))); task.setOutlineCode(3, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE3_INDEX))); task.setOutlineCode(4, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE4_INDEX))); task.setOutlineCode(5, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE5_INDEX))); task.setOutlineCode(6, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE6_INDEX))); task.setOutlineCode(7, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE7_INDEX))); task.setOutlineCode(8, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE8_INDEX))); task.setOutlineCode(9, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE9_INDEX))); task.setOutlineCode(10, getCustomFieldOutlineCodeValue(taskVarData, m_outlineCodeVarData, uniqueID, fieldMap.getVarDataKey(TaskField.OUTLINE_CODE10_INDEX))); task.setRollup((metaData[10] & 0x08) != 0); task.setUniqueID(uniqueID); switch (task.getConstraintType()) { // // Adjust the start and finish dates if the task // is constrained to start as late as possible. // case AS_LATE_AS_POSSIBLE: { if (DateHelper.compare(task.getStart(), task.getLateStart()) < 0) { task.setStart(task.getLateStart()); } if (DateHelper.compare(task.getFinish(), task.getLateFinish()) < 0) { task.setFinish(task.getLateFinish()); } break; } case START_NO_LATER_THAN: case FINISH_NO_LATER_THAN: { if (DateHelper.compare(task.getFinish(), task.getStart()) < 0) { task.setFinish(task.getLateFinish()); } break; } default: { break; } } // // Retrieve task recurring data // if (recurringData != null) { if (recurringTaskReader == null) { recurringTaskReader = new RecurringTaskReader(m_file.getProjectProperties()); } recurringTaskReader.processRecurringTask(task, recurringData); task.setRecurring(true); } // // Retrieve the task notes. // notes = task.getNotes(); if (notes != null) { //claur // if (m_reader.getPreserveNoteFormatting() == false) // { // notes = RtfHelper.strip(notes); // } task.setNotes(notes); } // // Set the calendar name // Integer calendarID = (Integer) task.getCachedValue(TaskField.CALENDAR_UNIQUE_ID); if (calendarID != null && calendarID.intValue() != -1) { ProjectCalendar calendar = m_file.getCalendarByUniqueID(calendarID); if (calendar != null) { task.setCalendar(calendar); } } // // Set the sub project flag // SubProject sp = m_taskSubProjects.get(task.getUniqueID()); task.setSubProject(sp); // // Set the external flag // if (sp != null) { task.setExternalTask(sp.isExternalTask(task.getUniqueID())); if (task.getExternalTask()) { task.setExternalTaskProject(sp.getFullPath()); } } // // If we have a WBS value from the MPP file, don't autogenerate // if (task.getWBS() != null) { autoWBS = false; } // // If this is a split task, allocate space for the split durations // if ((metaData[9] & 0x80) == 0) { task.setSplits(new LinkedList<DateRange>()); } // // Process any enterprise columns // processTaskEnterpriseColumns(uniqueID, task, taskVarData, metaData2); // Unfortunately it looks like 'null' tasks sometimes make it through. So let's check for to see if we // need to mark this task as a null task after all. if (task.getName() == null && ((task.getStart() == null || task.getStart().getTime() == MPPUtility.getEpochDate().getTime()) || (task.getFinish() == null || task.getFinish().getTime() == MPPUtility.getEpochDate().getTime()) /*|| (task.getCreateDate() == null || task.getCreateDate().getTime() == MPPUtility.getEpochDate().getTime())*//* Valid tasks can have a null create date */)) { // Remove this to avoid passing bad data to the client m_file.removeTask(task); task = m_file.addTask(); task.setNull(true); task.setUniqueID(uniqueID); task.setID(id); m_nullTaskOrder.put(task.getID(), task.getUniqueID()); //System.out.println(task); continue; } if (data2 == null || data2.length < 24) { m_nullTaskOrder.put(task.getID(), task.getUniqueID()); } else { Long key = Long.valueOf(MPPUtility.getLong(data2, 16)); m_taskOrder.put(key, task.getUniqueID()); } m_eventManager.fireTaskReadEvent(task); //dumpUnknownData(task.getUniqueID().toString(), UNKNOWN_TASK_DATA, data); //System.out.println(task); } // // Enable auto WBS if necessary // m_file.getProjectConfig().setAutoWBS(autoWBS); // // We have now read all of the task, so we are in a position // to perform post-processing to set up the relevant details // for each external task. // if (!externalTasks.isEmpty()) { processExternalTasks(externalTasks); } } /** * MPP14 files seem to exhibit some occasional weirdness * with duplicate ID values which leads to the task structure * being reported incorrectly. The following method attempts to correct this. * The method uses ordering data embedded in the file to reconstruct * the correct ID order of the tasks. */ private void postProcessTasks() throws MPXJException { // // Renumber ID values using a large increment to allow // space for later inserts. // TreeMap<Integer, Integer> taskMap = new TreeMap<Integer, Integer>(); int nextIDIncrement = 1000; int nextID = (m_file.getTaskByUniqueID(Integer.valueOf(0)) == null ? nextIDIncrement : 0); for (Map.Entry<Long, Integer> entry : m_taskOrder.entrySet()) { taskMap.put(Integer.valueOf(nextID), entry.getValue()); nextID += nextIDIncrement; } // // Insert any null tasks into the correct location // int insertionCount = 0; for (Map.Entry<Integer, Integer> entry : m_nullTaskOrder.entrySet()) { int idValue = entry.getKey().intValue(); int baseTargetIdValue = (idValue - insertionCount) * nextIDIncrement; int targetIDValue = baseTargetIdValue; int offset = 0; ++insertionCount; while (taskMap.containsKey(Integer.valueOf(targetIDValue))) { ++offset; if (offset == nextIDIncrement) { throw new MPXJException("Unable to fix task order"); } targetIDValue = baseTargetIdValue - (nextIDIncrement - offset); } taskMap.put(Integer.valueOf(targetIDValue), entry.getValue()); } // // Finally, we can renumber the tasks // nextID = (m_file.getTaskByUniqueID(Integer.valueOf(0)) == null ? 1 : 0); for (Map.Entry<Integer, Integer> entry : taskMap.entrySet()) { Task task = m_file.getTaskByUniqueID(entry.getValue()); if (task != null) { task.setID(Integer.valueOf(nextID)); } nextID++; } } /** * Extracts task enterprise column values. * * @param id task unique ID * @param task task instance * @param taskVarData task var data * @param metaData2 task meta data */ private void processTaskEnterpriseColumns(Integer id, Task task, Var2Data taskVarData, byte[] metaData2) { // task.setEnterpriseCost(1, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST1) / 100)); // task.setEnterpriseCost(2, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST2) / 100)); // task.setEnterpriseCost(3, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST3) / 100)); // task.setEnterpriseCost(4, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST4) / 100)); // task.setEnterpriseCost(5, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST5) / 100)); // task.setEnterpriseCost(6, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST6) / 100)); // task.setEnterpriseCost(7, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST7) / 100)); // task.setEnterpriseCost(8, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST8) / 100)); // task.setEnterpriseCost(9, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST9) / 100)); // task.setEnterpriseCost(10, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_COST10) / 100)); // task.setEnterpriseDate(1, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE1)); // task.setEnterpriseDate(2, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE2)); // task.setEnterpriseDate(3, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE3)); // task.setEnterpriseDate(4, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE4)); // task.setEnterpriseDate(5, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE5)); // task.setEnterpriseDate(6, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE6)); // task.setEnterpriseDate(7, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE7)); // task.setEnterpriseDate(8, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE8)); // task.setEnterpriseDate(9, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE9)); // task.setEnterpriseDate(10, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE10)); // task.setEnterpriseDate(11, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE11)); // task.setEnterpriseDate(12, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE12)); // task.setEnterpriseDate(13, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE13)); // task.setEnterpriseDate(14, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE14)); // task.setEnterpriseDate(15, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE15)); // task.setEnterpriseDate(16, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE16)); // task.setEnterpriseDate(17, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE17)); // task.setEnterpriseDate(18, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE18)); // task.setEnterpriseDate(19, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE19)); // task.setEnterpriseDate(20, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE20)); // task.setEnterpriseDate(21, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE21)); // task.setEnterpriseDate(22, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE22)); // task.setEnterpriseDate(23, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE23)); // task.setEnterpriseDate(24, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE24)); // task.setEnterpriseDate(25, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE25)); // task.setEnterpriseDate(26, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE26)); // task.setEnterpriseDate(27, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE27)); // task.setEnterpriseDate(28, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE28)); // task.setEnterpriseDate(29, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE29)); // task.setEnterpriseDate(30, taskVarData.getTimestamp(id, TASK_ENTERPRISE_DATE30)); // task.setEnterpriseDuration(1, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION1), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION1_UNITS)))); // task.setEnterpriseDuration(2, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION2), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION2_UNITS)))); // task.setEnterpriseDuration(3, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION3), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION3_UNITS)))); // task.setEnterpriseDuration(4, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION4), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION4_UNITS)))); // task.setEnterpriseDuration(5, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION5), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION5_UNITS)))); // task.setEnterpriseDuration(6, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION6), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION6_UNITS)))); // task.setEnterpriseDuration(7, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION7), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION7_UNITS)))); // task.setEnterpriseDuration(8, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION8), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION8_UNITS)))); // task.setEnterpriseDuration(9, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION9), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION9_UNITS)))); // task.setEnterpriseDuration(10, MPPUtility.getAdjustedDuration(m_file, taskVarData.getInt(id, TASK_ENTERPRISE_DURATION10), MPPUtility.getDurationTimeUnits(taskVarData.getShort(id, TASK_ENTERPRISE_DURATION10_UNITS)))); // task.setEnterpriseNumber(1, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER1))); // task.setEnterpriseNumber(2, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER2))); // task.setEnterpriseNumber(3, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER3))); // task.setEnterpriseNumber(4, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER4))); // task.setEnterpriseNumber(5, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER5))); // task.setEnterpriseNumber(6, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER6))); // task.setEnterpriseNumber(7, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER7))); // task.setEnterpriseNumber(8, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER8))); // task.setEnterpriseNumber(9, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER9))); // task.setEnterpriseNumber(10, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER10))); // task.setEnterpriseNumber(11, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER11))); // task.setEnterpriseNumber(12, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER12))); // task.setEnterpriseNumber(13, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER13))); // task.setEnterpriseNumber(14, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER14))); // task.setEnterpriseNumber(15, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER15))); // task.setEnterpriseNumber(16, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER16))); // task.setEnterpriseNumber(17, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER17))); // task.setEnterpriseNumber(18, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER18))); // task.setEnterpriseNumber(19, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER19))); // task.setEnterpriseNumber(20, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER20))); // task.setEnterpriseNumber(21, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER21))); // task.setEnterpriseNumber(22, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER22))); // task.setEnterpriseNumber(23, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER23))); // task.setEnterpriseNumber(24, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER24))); // task.setEnterpriseNumber(25, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER25))); // task.setEnterpriseNumber(26, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER26))); // task.setEnterpriseNumber(27, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER27))); // task.setEnterpriseNumber(28, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER28))); // task.setEnterpriseNumber(29, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER29))); // task.setEnterpriseNumber(30, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER30))); // task.setEnterpriseNumber(31, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER31))); // task.setEnterpriseNumber(32, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER32))); // task.setEnterpriseNumber(33, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER33))); // task.setEnterpriseNumber(34, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER34))); // task.setEnterpriseNumber(35, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER35))); // task.setEnterpriseNumber(36, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER36))); // task.setEnterpriseNumber(37, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER37))); // task.setEnterpriseNumber(38, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER38))); // task.setEnterpriseNumber(39, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER39))); // task.setEnterpriseNumber(40, NumberUtility.getDouble(taskVarData.getDouble(id, TASK_ENTERPRISE_NUMBER40))); // task.setEnterpriseText(1, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT1)); // task.setEnterpriseText(2, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT2)); // task.setEnterpriseText(3, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT3)); // task.setEnterpriseText(4, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT4)); // task.setEnterpriseText(5, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT5)); // task.setEnterpriseText(6, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT6)); // task.setEnterpriseText(7, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT7)); // task.setEnterpriseText(8, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT8)); // task.setEnterpriseText(9, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT9)); // task.setEnterpriseText(10, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT10)); // task.setEnterpriseText(11, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT11)); // task.setEnterpriseText(12, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT12)); // task.setEnterpriseText(13, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT13)); // task.setEnterpriseText(14, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT14)); // task.setEnterpriseText(15, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT15)); // task.setEnterpriseText(16, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT16)); // task.setEnterpriseText(17, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT17)); // task.setEnterpriseText(18, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT18)); // task.setEnterpriseText(19, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT19)); // task.setEnterpriseText(20, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT20)); // task.setEnterpriseText(21, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT21)); // task.setEnterpriseText(22, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT22)); // task.setEnterpriseText(23, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT23)); // task.setEnterpriseText(24, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT24)); // task.setEnterpriseText(25, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT25)); // task.setEnterpriseText(26, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT26)); // task.setEnterpriseText(27, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT27)); // task.setEnterpriseText(28, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT28)); // task.setEnterpriseText(29, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT29)); // task.setEnterpriseText(30, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT30)); // task.setEnterpriseText(31, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT31)); // task.setEnterpriseText(32, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT32)); // task.setEnterpriseText(33, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT33)); // task.setEnterpriseText(34, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT34)); // task.setEnterpriseText(35, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT35)); // task.setEnterpriseText(36, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT36)); // task.setEnterpriseText(37, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT37)); // task.setEnterpriseText(38, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT38)); // task.setEnterpriseText(39, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT39)); // task.setEnterpriseText(40, taskVarData.getUnicodeString(id, TASK_ENTERPRISE_TEXT40)); if (metaData2 != null) { int bits = MPPUtility.getInt(metaData2, 59); task.set(TaskField.ENTERPRISE_FLAG1, Boolean.valueOf((bits & 0x00001) != 0)); task.set(TaskField.ENTERPRISE_FLAG2, Boolean.valueOf((bits & 0x00002) != 0)); task.set(TaskField.ENTERPRISE_FLAG3, Boolean.valueOf((bits & 0x00004) != 0)); task.set(TaskField.ENTERPRISE_FLAG4, Boolean.valueOf((bits & 0x00008) != 0)); task.set(TaskField.ENTERPRISE_FLAG5, Boolean.valueOf((bits & 0x00010) != 0)); task.set(TaskField.ENTERPRISE_FLAG6, Boolean.valueOf((bits & 0x00020) != 0)); task.set(TaskField.ENTERPRISE_FLAG7, Boolean.valueOf((bits & 0x00040) != 0)); task.set(TaskField.ENTERPRISE_FLAG8, Boolean.valueOf((bits & 0x00080) != 0)); task.set(TaskField.ENTERPRISE_FLAG9, Boolean.valueOf((bits & 0x00100) != 0)); task.set(TaskField.ENTERPRISE_FLAG10, Boolean.valueOf((bits & 0x00200) != 0)); task.set(TaskField.ENTERPRISE_FLAG11, Boolean.valueOf((bits & 0x00400) != 0)); task.set(TaskField.ENTERPRISE_FLAG12, Boolean.valueOf((bits & 0x00800) != 0)); task.set(TaskField.ENTERPRISE_FLAG13, Boolean.valueOf((bits & 0x01000) != 0)); task.set(TaskField.ENTERPRISE_FLAG14, Boolean.valueOf((bits & 0x02000) != 0)); task.set(TaskField.ENTERPRISE_FLAG15, Boolean.valueOf((bits & 0x04000) != 0)); task.set(TaskField.ENTERPRISE_FLAG16, Boolean.valueOf((bits & 0x08000) != 0)); task.set(TaskField.ENTERPRISE_FLAG17, Boolean.valueOf((bits & 0x10000) != 0)); task.set(TaskField.ENTERPRISE_FLAG18, Boolean.valueOf((bits & 0x20000) != 0)); task.set(TaskField.ENTERPRISE_FLAG19, Boolean.valueOf((bits & 0x40000) != 0)); task.set(TaskField.ENTERPRISE_FLAG20, Boolean.valueOf((bits & 0x80000) != 0)); } } /** * Extracts resource enterprise column data. * * @param resource resource instance * @param metaData2 resource meta data */ private void processResourceEnterpriseColumns(Resource resource, byte[] metaData2) { if (metaData2 != null) { int bits = MPPUtility.getInt(metaData2, 16); resource.set(ResourceField.ENTERPRISE_FLAG1, Boolean.valueOf((bits & 0x00010) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG2, Boolean.valueOf((bits & 0x00020) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG3, Boolean.valueOf((bits & 0x00040) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG4, Boolean.valueOf((bits & 0x00080) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG5, Boolean.valueOf((bits & 0x00100) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG6, Boolean.valueOf((bits & 0x00200) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG7, Boolean.valueOf((bits & 0x00400) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG8, Boolean.valueOf((bits & 0x00800) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG9, Boolean.valueOf((bits & 0x01000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG10, Boolean.valueOf((bits & 0x02000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG11, Boolean.valueOf((bits & 0x04000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG12, Boolean.valueOf((bits & 0x08000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG13, Boolean.valueOf((bits & 0x10000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG14, Boolean.valueOf((bits & 0x20000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG15, Boolean.valueOf((bits & 0x40000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG16, Boolean.valueOf((bits & 0x80000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG17, Boolean.valueOf((bits & 0x100000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG18, Boolean.valueOf((bits & 0x200000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG19, Boolean.valueOf((bits & 0x400000) != 0)); resource.set(ResourceField.ENTERPRISE_FLAG20, Boolean.valueOf((bits & 0x800000) != 0)); } } /** * The project files to which external tasks relate appear not to be * held against each task, instead there appears to be the concept * of the "current" external task file, i.e. the last one used. * This method iterates through the list of tasks marked as external * and attempts to ensure that the correct external project data (in the * form of a SubProject object) is linked to the task. * * @param externalTasks list of tasks marked as external */ private void processExternalTasks(List<Task> externalTasks) { // // Sort the list of tasks into ID order // Collections.sort(externalTasks); // // Find any external tasks which don't have a sub project // object, and set this attribute using the most recent // value. // SubProject currentSubProject = null; for (Task currentTask : externalTasks) { SubProject sp = currentTask.getSubProject(); if (sp == null) { currentTask.setSubProject(currentSubProject); //we need to set the external task project path now that we have //the subproject for this task (was skipped while processing the task earlier) if (currentSubProject != null) { currentTask.setExternalTaskProject(currentSubProject.getFullPath()); } } else { currentSubProject = sp; } if (currentSubProject != null) { //System.out.println ("Task: " +currentTask.getUniqueID() + " " + currentTask.getName() + " File=" + currentSubProject.getFullPath() + " ID=" + currentTask.getExternalTaskID()); currentTask.setProject(currentSubProject.getFullPath()); } } } /** * This method is used to extract the task hyperlink attributes * from a block of data and call the appropriate modifier methods * to configure the specified task object. * * @param task task instance * @param data hyperlink data block */ private void processHyperlinkData(Task task, byte[] data) { if (data != null) { int offset = 12; String hyperlink; String address; String subaddress; offset += 12; hyperlink = MPPUtility.getUnicodeString(data, offset); offset += ((hyperlink.length() + 1) * 2); offset += 12; address = MPPUtility.getUnicodeString(data, offset); offset += ((address.length() + 1) * 2); offset += 12; subaddress = MPPUtility.getUnicodeString(data, offset); task.setHyperlink(hyperlink); task.setHyperlinkAddress(address); task.setHyperlinkSubAddress(subaddress); } } /** * This method is used to extract the resource hyperlink attributes * from a block of data and call the appropriate modifier methods * to configure the specified task object. * * @param resource resource instance * @param data hyperlink data block */ private void processHyperlinkData(Resource resource, byte[] data) { if (data != null) { int offset = 12; String hyperlink; String address; String subaddress; offset += 12; hyperlink = MPPUtility.getUnicodeString(data, offset); offset += ((hyperlink.length() + 1) * 2); offset += 12; address = MPPUtility.getUnicodeString(data, offset); offset += ((address.length() + 1) * 2); offset += 12; subaddress = MPPUtility.getUnicodeString(data, offset); resource.setHyperlink(hyperlink); resource.setHyperlinkAddress(address); resource.setHyperlinkSubAddress(subaddress); } } /** * This method extracts and collates constraint data. * * @throws java.io.IOException */ private void processConstraintData() throws IOException { ConstraintFactory factory = new ConstraintFactory(); factory.process(m_projectDir, m_file, m_inputStreamFactory); } /** * This method extracts and collates resource data. * * @throws java.io.IOException */ private void processResourceData() throws IOException { FieldMap fieldMap = new FieldMap12(m_file.getProjectProperties(), m_file.getCustomFields()); fieldMap.createResourceFieldMap(m_projectProps); FieldMap enterpriseCustomFieldMap = new FieldMap12(m_file.getProjectProperties(), m_file.getCustomFields()); enterpriseCustomFieldMap.createEnterpriseCustomFieldMap(m_projectProps, ResourceField.class); DirectoryEntry rscDir = (DirectoryEntry) m_projectDir.getEntry("TBkndRsc"); VarMeta rscVarMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) rscDir.getEntry("VarMeta")))); Var2Data rscVarData = new Var2Data(rscVarMeta, new DocumentInputStream(((DocumentEntry) rscDir.getEntry("Var2Data")))); FixedMeta rscFixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) rscDir.getEntry("FixedMeta"))), 37); FixedData rscFixedData = new FixedData(rscFixedMeta, m_inputStreamFactory.getInstance(rscDir, "FixedData")); FixedMeta rscFixed2Meta = new FixedMeta(new DocumentInputStream(((DocumentEntry) rscDir.getEntry("Fixed2Meta"))), 49); FixedData rscFixed2Data = new FixedData(rscFixed2Meta, m_inputStreamFactory.getInstance(rscDir, "Fixed2Data")); Props12 props = new Props12(m_inputStreamFactory.getInstance(rscDir, "Props")); //System.out.println(rscVarMeta); //System.out.println(rscVarData); //System.out.println(rscFixedMeta); //System.out.println(rscFixedData); //System.out.println(rscFixed2Meta); //System.out.println(rscFixed2Data); //System.out.println(props); // Process aliases new CustomFieldAliasReader(m_file.getCustomFields(), props.getByteArray(RESOURCE_FIELD_NAME_ALIASES)).process(); TreeMap<Integer, Integer> resourceMap = createResourceMap(fieldMap, rscFixedMeta, rscFixedData); Integer[] uniqueid = rscVarMeta.getUniqueIdentifierArray(); Integer id; Integer offset; byte[] data; byte[] metaData; Resource resource; String notes; for (int loop = 0; loop < uniqueid.length; loop++) { id = uniqueid[loop]; offset = resourceMap.get(id); if (offset == null) { continue; } data = rscFixedData.getByteArrayValue(offset.intValue()); byte[] metaData2 = rscFixed2Meta.getByteArrayValue(offset.intValue()); byte[] data2 = rscFixed2Data.getByteArrayValue(offset.intValue()); //metaData = rscFixedMeta.getByteArrayValue(offset.intValue()); //MPPUtility.dataDump(data, true, true, true, true, true, true, true); //MPPUtility.dataDump(metaData, true, true, true, true, true, true, true); //MPPUtility.varDataDump(rscVarData, id, true, true, true, true, true, true); resource = m_file.addResource(); resource.disableEvents(); fieldMap.populateContainer(ResourceField.class, resource, id, new byte[][] { data, data2 }, rscVarData); enterpriseCustomFieldMap.populateContainer(ResourceField.class, resource, id, null, rscVarData); resource.enableEvents(); resource.setBudget((metaData2[8] & 0x20) != 0); resource.setGUID(MPPUtility.getGUID(data2, 0)); processHyperlinkData(resource, rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.HYPERLINK_DATA))); resource.setID(Integer.valueOf(MPPUtility.getInt(data, 4))); resource.setOutlineCode1(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE1_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode2(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE2_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode3(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE3_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode4(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE4_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode5(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE5_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode6(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE6_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode7(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE7_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode8(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE8_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode9(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE9_INDEX))), OUTLINECODE_DATA)); resource.setOutlineCode10(m_outlineCodeVarData.getUnicodeString(Integer.valueOf(rscVarData.getInt(id, 2, fieldMap.getVarDataKey(ResourceField.OUTLINE_CODE10_INDEX))), OUTLINECODE_DATA)); resource.setType((resource.getWorkGroup() == WorkGroup.DEFAULT ? ResourceType.WORK : ((metaData2[8] & 0x10) == 0) ? ResourceType.MATERIAL : ResourceType.COST)); resource.setUniqueID(id); metaData = rscFixedMeta.getByteArrayValue(offset.intValue()); resource.setFlag(1, (metaData[28] & 0x40) != 0); resource.setFlag(2, (metaData[28] & 0x80) != 0); resource.setFlag(3, (metaData[29] & 0x01) != 0); resource.setFlag(4, (metaData[29] & 0x02) != 0); resource.setFlag(5, (metaData[29] & 0x04) != 0); resource.setFlag(6, (metaData[29] & 0x08) != 0); resource.setFlag(7, (metaData[29] & 0x10) != 0); resource.setFlag(8, (metaData[29] & 0x20) != 0); resource.setFlag(9, (metaData[29] & 0x40) != 0); resource.setFlag(10, (metaData[28] & 0x20) != 0); resource.setFlag(11, (metaData[29] & 0x20) != 0); resource.setFlag(12, (metaData[30] & 0x01) != 0); resource.setFlag(13, (metaData[30] & 0x02) != 0); resource.setFlag(14, (metaData[30] & 0x04) != 0); resource.setFlag(15, (metaData[30] & 0x08) != 0); resource.setFlag(16, (metaData[30] & 0x10) != 0); resource.setFlag(17, (metaData[30] & 0x20) != 0); resource.setFlag(18, (metaData[30] & 0x40) != 0); resource.setFlag(19, (metaData[30] & 0x80) != 0); resource.setFlag(20, (metaData[31] & 0x01) != 0); notes = resource.getNotes(); //claur // if (m_reader.getPreserveNoteFormatting() == false) // { // notes = RtfHelper.strip(notes); // } resource.setNotes(notes); // // Configure the resource calendar // resource.setResourceCalendar(m_resourceMap.get(id)); // // Process any enterprise columns // processResourceEnterpriseColumns(resource, metaData2); // // Process cost rate tables // CostRateTableFactory crt = new CostRateTableFactory(); crt.process(resource, 0, rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.COST_RATE_A))); crt.process(resource, 1, rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.COST_RATE_B))); crt.process(resource, 2, rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.COST_RATE_C))); crt.process(resource, 3, rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.COST_RATE_D))); crt.process(resource, 4, rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.COST_RATE_E))); // // Process availability table // AvailabilityFactory af = new AvailabilityFactory(); af.process(resource.getAvailability(), rscVarData.getByteArray(id, fieldMap.getVarDataKey(ResourceField.AVAILABILITY_DATA))); m_eventManager.fireResourceReadEvent(resource); } } /** * This method extracts and collates resource assignment data. * * @throws IOException */ private void processAssignmentData() throws IOException { FieldMap fieldMap = new FieldMap12(m_file.getProjectProperties(), m_file.getCustomFields()); fieldMap.createAssignmentFieldMap(m_projectProps); FieldMap enterpriseCustomFieldMap = new FieldMap12(m_file.getProjectProperties(), m_file.getCustomFields()); enterpriseCustomFieldMap.createEnterpriseCustomFieldMap(m_projectProps, AssignmentField.class); DirectoryEntry assnDir = (DirectoryEntry) m_projectDir.getEntry("TBkndAssn"); VarMeta assnVarMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) assnDir.getEntry("VarMeta")))); Var2Data assnVarData = new Var2Data(assnVarMeta, new DocumentInputStream(((DocumentEntry) assnDir.getEntry("Var2Data")))); FixedMeta assnFixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) assnDir.getEntry("FixedMeta"))), 34); // MSP 20007 seems to write 142 byte blocks, MSP 2010 writes 110 byte blocks // We need to identify any cases where the meta data count does not correctly identify the block size FixedData assnFixedData = new FixedData(assnFixedMeta, m_inputStreamFactory.getInstance(assnDir, "FixedData")); FixedData assnFixedData2 = new FixedData(48, m_inputStreamFactory.getInstance(assnDir, "Fixed2Data")); ResourceAssignmentFactory factory = new ResourceAssignmentFactory(); factory.process(m_file, fieldMap, enterpriseCustomFieldMap, m_reader.getUseRawTimephasedData(), m_reader.getPreserveNoteFormatting(), assnVarMeta, assnVarData, assnFixedMeta, assnFixedData, assnFixedData2, assnFixedMeta.getAdjustedItemCount()); } /** * This method is used to determine if a duration is estimated. * * @param type Duration units value * @return boolean Estimated flag */ private boolean getDurationEstimated(int type) { return ((type & DURATION_CONFIRMED_MASK) != 0); } /** * This method extracts view data from the MPP file. * * @throws java.io.IOException */ private void processViewData() throws IOException { DirectoryEntry dir = (DirectoryEntry) m_viewDir.getEntry("CV_iew"); VarMeta viewVarMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) dir.getEntry("VarMeta")))); Var2Data viewVarData = new Var2Data(viewVarMeta, new DocumentInputStream(((DocumentEntry) dir.getEntry("Var2Data")))); FixedMeta fixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) dir.getEntry("FixedMeta"))), 10); FixedData fixedData = new FixedData(138, m_inputStreamFactory.getInstance(dir, "FixedData")); int items = fixedMeta.getAdjustedItemCount(); View view; ViewFactory factory = new ViewFactory12(); int lastOffset = -1; for (int loop = 0; loop < items; loop++) { byte[] fm = fixedMeta.getByteArrayValue(loop); int offset = MPPUtility.getShort(fm, 4); if (offset > lastOffset) { byte[] fd = fixedData.getByteArrayValue(fixedData.getIndexFromOffset(offset)); if (fd != null) { view = factory.createView(m_file, fm, fd, viewVarData, m_fontBases); m_file.getViews().add(view); } lastOffset = offset; } } } /** * This method extracts table data from the MPP file. * * @throws java.io.IOException */ private void processTableData() throws IOException { DirectoryEntry dir = (DirectoryEntry) m_viewDir.getEntry("CTable"); VarMeta varMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) dir.getEntry("VarMeta")))); Var2Data varData = new Var2Data(varMeta, new DocumentInputStream(((DocumentEntry) dir.getEntry("Var2Data")))); FixedData fixedData = new FixedData(230, new DocumentInputStream(((DocumentEntry) dir.getEntry("FixedData")))); //System.out.println(varMeta); //System.out.println(varData); //System.out.println(fixedData); TableContainer container = m_file.getTables(); TableFactory factory = new TableFactory(TABLE_COLUMN_DATA_STANDARD, TABLE_COLUMN_DATA_ENTERPRISE, TABLE_COLUMN_DATA_BASELINE); int items = fixedData.getItemCount(); for (int loop = 0; loop < items; loop++) { byte[] data = fixedData.getByteArrayValue(loop); Table table = factory.createTable(m_file, data, varMeta, varData); container.add(table); //System.out.println(table); } } /** * Read filter definitions. * * @throws IOException */ private void processFilterData() throws IOException { DirectoryEntry dir = (DirectoryEntry) m_viewDir.getEntry("CFilter"); FixedMeta fixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) dir.getEntry("FixedMeta"))), 10); FixedData fixedData = new FixedData(fixedMeta, m_inputStreamFactory.getInstance(dir, "FixedData")); VarMeta varMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) dir.getEntry("VarMeta")))); Var2Data varData = new Var2Data(varMeta, new DocumentInputStream(((DocumentEntry) dir.getEntry("Var2Data")))); //System.out.println(fixedMeta); //System.out.println(fixedData); //System.out.println(varMeta); //System.out.println(varData); FilterReader reader = new FilterReader12(); reader.process(m_file.getProjectProperties(), m_file.getFilters(), fixedData, varData); } /** * Read saved view state from an MPP file. * * @throws IOException */ private void processSavedViewState() throws IOException { DirectoryEntry dir = (DirectoryEntry) m_viewDir.getEntry("CEdl"); VarMeta varMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) dir.getEntry("VarMeta")))); Var2Data varData = new Var2Data(varMeta, new DocumentInputStream(((DocumentEntry) dir.getEntry("Var2Data")))); //System.out.println(varMeta); //System.out.println(varData); InputStream is = new DocumentInputStream(((DocumentEntry) dir.getEntry("FixedData"))); byte[] fixedData = new byte[is.available()]; is.read(fixedData); is.close(); //System.out.println(MPPUtility.hexdump(fixedData, false, 16, "")); ViewStateReader reader = new ViewStateReader12(); reader.process(m_file, varData, fixedData); } /** * Read group definitions. * * @todo Doesn't work correctly with MPP12 files saved by Project 2007 and 2010 * @throws IOException */ private void processGroupData() throws IOException { DirectoryEntry dir = (DirectoryEntry) m_viewDir.getEntry("CGrouping"); FixedMeta fixedMeta = new FixedMeta(new DocumentInputStream(((DocumentEntry) dir.getEntry("FixedMeta"))), 10); FixedData fixedData = new FixedData(fixedMeta, m_inputStreamFactory.getInstance(dir, "FixedData")); VarMeta varMeta = new VarMeta12(new DocumentInputStream(((DocumentEntry) dir.getEntry("VarMeta")))); Var2Data varData = new Var2Data(varMeta, new DocumentInputStream(((DocumentEntry) dir.getEntry("Var2Data")))); // System.out.println(fixedMeta); // System.out.println(fixedData); // System.out.println(varMeta); // System.out.println(varData); GroupReader reader = new GroupReader12(); reader.process(m_file, fixedData, varData, m_fontBases); } /** * Retrieve custom field value. * * @param varData var data block * @param outlineCodeVarData var data block * @param id item ID * @param type item type * @return item value */ private String getCustomFieldOutlineCodeValue(Var2Data varData, Var2Data outlineCodeVarData, Integer id, Integer type) { String result = null; int mask = varData.getShort(id, type); if ((mask & 0xFF00) != VALUE_LIST_MASK) { result = outlineCodeVarData.getUnicodeString(Integer.valueOf(varData.getInt(id, 2, type)), OUTLINECODE_DATA); } else { int uniqueId = varData.getInt(id, 2, type); CustomFieldValueItem item = m_file.getCustomFields().getCustomFieldValueItemByUniqueID(uniqueId); if (item != null) { Object value = item.getValue(); if (value instanceof String) { result = (String) value; } String result2 = getCustomFieldOutlineCodeValue(varData, outlineCodeVarData, item.getParent()); if (result2 != null && !result2.isEmpty()) { result = result2 + "." + result; } } } return result; } /** * Retrieve custom field value. * * @param varData var data block * @param outlineCodeVarData var data block * @param id parent item ID * @return item value */ private String getCustomFieldOutlineCodeValue(Var2Data varData, Var2Data outlineCodeVarData, Integer id) { String result = null; int uniqueId = id.intValue(); if (uniqueId == 0) { return ""; } CustomFieldValueItem item = m_file.getCustomFields().getCustomFieldValueItemByUniqueID(uniqueId); if (item != null) { Object value = item.getValue(); if (value instanceof String) { result = (String) value; } if (result != null && !NumberHelper.equals(id, item.getParent())) { String result2 = getCustomFieldOutlineCodeValue(varData, outlineCodeVarData, item.getParent()); if (result2 != null && !result2.isEmpty()) { result = result2 + "." + result; } } } return result; } private MPPReader m_reader; private ProjectFile m_file; private EventManager m_eventManager; private DirectoryEntry m_root; private HashMap<Integer, ProjectCalendar> m_resourceMap; private Var2Data m_outlineCodeVarData; private VarMeta m_outlineCodeVarMeta; private FixedData m_outlineCodeFixedData; private FixedMeta m_outlineCodeFixedMeta; private FixedData m_outlineCodeFixedData2; private FixedMeta m_outlineCodeFixedMeta2; private Props12 m_projectProps; private Map<Integer, FontBase> m_fontBases; private Map<Integer, SubProject> m_taskSubProjects; private DirectoryEntry m_projectDir; private DirectoryEntry m_viewDir; private Map<Long, Integer> m_taskOrder; private Map<Integer, Integer> m_nullTaskOrder; private DocumentInputStreamFactory m_inputStreamFactory; // Signals the end of the list of subproject task unique ids //private static final int SUBPROJECT_LISTEND = 0x00000303; // Signals that the previous value was for the subproject task unique id private static final int SUBPROJECT_TASKUNIQUEID0 = 0x00000000; private static final int SUBPROJECT_TASKUNIQUEID1 = 0x0B340000; private static final int SUBPROJECT_TASKUNIQUEID2 = 0x0ABB0000; private static final int SUBPROJECT_TASKUNIQUEID3 = 0x05A10000; private static final int SUBPROJECT_TASKUNIQUEID4 = 0x0BD50000; private static final int SUBPROJECT_TASKUNIQUEID5 = 0x03D60000; private static final int SUBPROJECT_TASKUNIQUEID6 = 0x07010000; /** * Calendar data types. */ private static final Integer CALENDAR_NAME = Integer.valueOf(1); private static final Integer CALENDAR_DATA = Integer.valueOf(8); /** * Resource data types. */ private static final Integer TABLE_COLUMN_DATA_STANDARD = Integer.valueOf(6); private static final Integer TABLE_COLUMN_DATA_ENTERPRISE = Integer.valueOf(7); private static final Integer TABLE_COLUMN_DATA_BASELINE = Integer.valueOf(8); /** * Outline code data types. */ private static final Integer OUTLINECODE_DATA = Integer.valueOf(22); /** * Custom value list data types. */ private static final int VALUE_LIST_MASK = 0x0700; /** * Mask used to isolate confirmed flag from the duration units field. */ private static final int DURATION_CONFIRMED_MASK = 0x20; /** * Deleted and null tasks have their ID and UniqueID attributes at fixed offsets. */ private static final int TASK_UNIQUE_ID_FIXED_OFFSET = 0; private static final int TASK_ID_FIXED_OFFSET = 4; private static final int NULL_TASK_BLOCK_SIZE = 16; /** * Alias data types. */ private static final Integer RESOURCE_FIELD_NAME_ALIASES = Integer.valueOf(71303169); private static final Integer TASK_FIELD_NAME_ALIASES = Integer.valueOf(71303169); /** * Default working week. */ private static final boolean[] DEFAULT_WORKING_WEEK = { false, true, true, true, true, true, false }; }