net.sf.mpxj.mpp.CustomFieldValueReader9 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mpxj Show documentation
Show all versions of mpxj Show documentation
Library that provides facilities to allow project information to be manipulated in Java and .Net. Supports a range of data formats: Microsoft Project Exchange (MPX), Microsoft Project (MPP,MPT), Microsoft Project Data Interchange (MSPDI XML), Microsoft Project Database (MPD), Planner (XML), Primavera (PM XML, XER, and database), Asta Powerproject (PP, MDB), Asta Easyplan (PP), Phoenix Project Manager (PPX), FastTrack Schedule (FTS), and the Standard Data Exchange Format (SDEF).
/*
* file: CustomFieldValueReader12.java
* author: Jon Iles
* copyright: (c) Packwood Software 2015
* date: 28/04/2015
*/
/*
* 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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import net.sf.mpxj.ProjectFile;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import net.sf.mpxj.CustomField;
import net.sf.mpxj.CustomFieldContainer;
import net.sf.mpxj.CustomFieldLookupTable;
import net.sf.mpxj.CustomFieldValueDataType;
import net.sf.mpxj.DataType;
import net.sf.mpxj.Duration;
import net.sf.mpxj.FieldType;
import net.sf.mpxj.FieldTypeClass;
import net.sf.mpxj.ProjectProperties;
import net.sf.mpxj.TimeUnit;
import net.sf.mpxj.common.FieldTypeHelper;
import net.sf.mpxj.common.Pair;
/**
* MPP9 custom field value reader.
*/
public class CustomFieldValueReader9
{
/**
* Constructor.
*
* @param projectDir project directory
* @param file project file
* @param projectProps MPP project properties
*/
public CustomFieldValueReader9(DirectoryEntry projectDir, ProjectFile file, Props projectProps)
{
m_projectDir = projectDir;
m_file = file;
m_properties = file.getProjectProperties();
m_projectProps = projectProps;
m_container = file.getCustomFields();
}
/**
* Reads custom field values and populates container.
*/
public void process() throws IOException
{
processCustomFieldValues();
processOutlineCodeValues();
}
/**
* Reads non outline code custom field values and populates container.
*/
private void processCustomFieldValues()
{
byte[] data = m_projectProps.getByteArray(Props.TASK_FIELD_ATTRIBUTES);
if (data != null)
{
int index = 0;
int offset = 0;
// First the length
int length = MPPUtility.getInt(data, offset);
offset += 4;
// Then the number of custom value lists
int numberOfValueLists = MPPUtility.getInt(data, offset);
offset += 4;
// Then the value lists themselves
FieldType field;
int valueListOffset;
while (index < numberOfValueLists && offset < length)
{
// Each item consists of the Field ID (4 bytes) and the offset to the value list (4 bytes)
// Get the Field
field = FieldTypeHelper.getInstance(m_file, MPPUtility.getInt(data, offset));
offset += 4;
// Get the value list offset
valueListOffset = MPPUtility.getInt(data, offset);
offset += 4;
// Read the value list itself
if (valueListOffset < data.length)
{
int tempOffset = valueListOffset;
tempOffset += 8;
// Get the data offset
int dataOffset = MPPUtility.getInt(data, tempOffset) + valueListOffset;
tempOffset += 4;
// Get the end of the data offset
int endDataOffset = MPPUtility.getInt(data, tempOffset) + valueListOffset;
tempOffset += 4;
// Get the end of the description
int endDescriptionOffset = MPPUtility.getInt(data, tempOffset) + valueListOffset;
// Get the values themselves
int valuesLength = endDataOffset - dataOffset;
byte[] values = new byte[valuesLength];
MPPUtility.getByteArray(data, dataOffset, valuesLength, values, 0);
// Get the descriptions
int descriptionsLength = endDescriptionOffset - endDataOffset;
byte[] descriptions = new byte[descriptionsLength];
MPPUtility.getByteArray(data, endDataOffset, descriptionsLength, descriptions, 0);
populateContainer(field, values, descriptions);
}
index++;
}
}
}
/**
* Reads outline code custom field values and populates container.
*/
private void processOutlineCodeValues() throws IOException
{
DirectoryEntry outlineCodeDir = (DirectoryEntry) m_projectDir.getEntry("TBkndOutlCode");
FixedMeta fm = new FixedMeta(new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("FixedMeta"))), 10);
FixedData fd = new FixedData(fm, new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("FixedData"))));
Map map = new HashMap<>();
int items = fm.getItemCount();
for (int loop = 0; loop < items; loop++)
{
byte[] data = fd.getByteArrayValue(loop);
if (data.length < 18)
{
continue;
}
int fieldID = MPPUtility.getInt(data, 12);
FieldType fieldType = FieldTypeHelper.getInstance(m_file, fieldID);
if (fieldType != null && fieldType.getFieldTypeClass() != FieldTypeClass.UNKNOWN)
{
int index = MPPUtility.getShort(data, 0);
map.put(Integer.valueOf(index), fieldType);
}
}
VarMeta outlineCodeVarMeta = new VarMeta9(new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("VarMeta"))));
Var2Data outlineCodeVarData = new Var2Data(m_file, outlineCodeVarMeta, new DocumentInputStream(((DocumentEntry) outlineCodeDir.getEntry("Var2Data"))));
Map>> valueMap = new HashMap<>();
for (Integer id : outlineCodeVarMeta.getUniqueIdentifierArray())
{
FieldType fieldType = map.get(id);
String value = outlineCodeVarData.getUnicodeString(id, VALUE);
String description = outlineCodeVarData.getUnicodeString(id, DESCRIPTION);
List> list = valueMap.computeIfAbsent(fieldType, k -> new ArrayList<>());
list.add(new Pair<>(value, description));
}
for (Entry>> entry : valueMap.entrySet())
{
populateContainer(entry.getKey(), entry.getValue());
}
}
/**
* Populate the container, converting raw data into Java types.
*
* @param field custom field to which these values belong
* @param values raw value data
* @param descriptions raw description data
*/
private void populateContainer(FieldType field, byte[] values, byte[] descriptions)
{
CustomField config = m_container.getOrCreate(field);
CustomFieldLookupTable table = config.getLookupTable();
String fieldTypeName = config.getFieldType().getName();
table.setGUID(UUID.nameUUIDFromBytes(fieldTypeName.getBytes()));
List