/*
* file: PrimaveraXERFileReader.java
* author: Jon Iles
* copyright: (c) Packwood Software 2010
* date: 25/03/2010
*/
/*
* 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.primavera;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.sf.mpxj.FieldType;
import net.sf.mpxj.MPXJException;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.common.InputStreamTokenizer;
import net.sf.mpxj.common.NumberHelper;
import net.sf.mpxj.common.Tokenizer;
import net.sf.mpxj.listener.ProjectListener;
import net.sf.mpxj.reader.AbstractProjectReader;
/**
* This class creates a new ProjectFile instance by reading a Primavera XER file.
*/
public final class PrimaveraXERFileReader extends AbstractProjectReader
{
/**
* {@inheritDoc}
*/
@Override public void addProjectListener(ProjectListener listener)
{
if (m_projectListeners == null)
{
m_projectListeners = new LinkedList<ProjectListener>();
}
m_projectListeners.add(listener);
}
/**
* Set the ID of the project to be read.
*
* @param projectID project ID
*/
public void setProjectID(int projectID)
{
m_projectID = Integer.valueOf(projectID);
}
/**
* {@inheritDoc}
*/
@Override public ProjectFile read(InputStream is) throws MPXJException
{
try
{
m_tables = new HashMap<String, List<Row>>();
m_numberFormat = new DecimalFormat();
processFile(is);
m_reader = new PrimaveraReader(m_udfCounters, m_resourceFields, m_wbsFields, m_taskFields, m_assignmentFields, m_aliases, m_matchPrimaveraWBS);
ProjectFile project = m_reader.getProject();
project.getEventManager().addProjectListeners(m_projectListeners);
processProjectID();
processProjectProperties();
processUserDefinedFields();
processCalendars();
processResources();
processTasks();
processPredecessors();
processAssignments();
m_reader = null;
project.updateStructure();
return (project);
}
finally
{
m_reader = null;
m_tables = null;
m_currentTableName = null;
m_currentTable = null;
m_currentFieldNames = null;
m_defaultCurrencyName = null;
m_currencyMap.clear();
m_numberFormat = null;
m_defaultCurrencyData = null;
}
}
/**
* This is a convenience method which allows all projects in an
* XER file to be read in a single pass.
*
* @param is input stream
* @return list of ProjectFile instances
* @throws MPXJException
*/
public List<ProjectFile> readAll(InputStream is) throws MPXJException
{
try
{
List<ProjectFile> result = new LinkedList<ProjectFile>();
m_tables = new HashMap<String, List<Row>>();
m_numberFormat = new DecimalFormat();
processFile(is);
List<Row> rows = getRows("project", null, null);
for (Row row : rows)
{
setProjectID(row.getInt("proj_id"));
m_reader = new PrimaveraReader(m_udfCounters, m_resourceFields, m_wbsFields, m_taskFields, m_assignmentFields, m_aliases, m_matchPrimaveraWBS);
ProjectFile project = m_reader.getProject();
project.getEventManager().addProjectListeners(m_projectListeners);
processProjectProperties();
processUserDefinedFields();
processCalendars();
processResources();
processTasks();
processPredecessors();
processAssignments();
m_reader = null;
project.updateStructure();
result.add(project);
}
return result;
}
finally
{
m_reader = null;
m_tables = null;
m_currentTableName = null;
m_currentTable = null;
m_currentFieldNames = null;
m_defaultCurrencyName = null;
m_currencyMap.clear();
m_numberFormat = null;
m_defaultCurrencyData = null;
}
}
/**
* Reads the XER file table and row structure ready for processing.
*
* @param is input stream
* @throws MPXJException
*/
private void processFile(InputStream is) throws MPXJException
{
int line = 1;
try
{
//
// Test the header and extract the separator. If this is successful,
// we reset the stream back as far as we can. The design of the
// BufferedInputStream class means that we can't get back to character
// zero, so the first record we will read will get "RMHDR" rather than
// "ERMHDR" in the first field position.
//
BufferedInputStream bis = new BufferedInputStream(is);
byte[] data = new byte[6];
data[0] = (byte) bis.read();
bis.mark(1024);
bis.read(data, 1, 5);
if (!new String(data).equals("ERMHDR"))
{
throw new MPXJException(MPXJException.INVALID_FILE);
}
bis.reset();
Tokenizer tk = new InputStreamTokenizer(bis);
tk.setDelimiter('\t');
List<String> record = new ArrayList<String>();
while (tk.getType() != Tokenizer.TT_EOF)
{
readRecord(tk, record);
if (processRecord(record))
{
break;
}
++line;
}
}
catch (Exception ex)
{
throw new MPXJException(MPXJException.READ_ERROR + " (failed at line " + line + ")", ex);
}
}
/**
* If the user has not specified a project ID, this method
* retrieves the ID of the first project in the file.
*/
private void processProjectID()
{
if (m_projectID == null)
{
List<Row> rows = getRows("project", null, null);
if (!rows.isEmpty())
{
Row row = rows.get(0);
m_projectID = row.getInteger("proj_id");
}
}
}
/**
* Process a currency definition.
*
* @param row record from XER file
*/
private void processCurrency(Row row)
{
String currencyName = row.getString("curr_short_name");
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(row.getString("decimal_symbol").charAt(0));
symbols.setGroupingSeparator(row.getString("digit_group_symbol").charAt(0));
DecimalFormat nf = new DecimalFormat();
nf.setDecimalFormatSymbols(symbols);
nf.applyPattern("#.#");
m_currencyMap.put(currencyName, nf);
if (currencyName.equalsIgnoreCase(m_defaultCurrencyName))
{
m_numberFormat = nf;
m_defaultCurrencyData = row;
}
}
/**
* Populates a Map instance representing the IDs and names of
* projects available in the current file.
*
* @param is input stream used to read XER file
* @return Map instance containing ID and name pairs
* @throws MPXJException
*/
public Map<Integer, String> listProjects(InputStream is) throws MPXJException
{
try
{
m_tables = new HashMap<String, List<Row>>();
processFile(is);
Map<Integer, String> result = new HashMap<Integer, String>();
List<Row> rows = getRows("project", null, null);
for (Row row : rows)
{
Integer id = row.getInteger("proj_id");
String name = row.getString("proj_short_name");
result.put(id, name);
}
return result;
}
finally
{
m_tables = null;
m_currentTable = null;
m_currentFieldNames = null;
}
}
/**
* Process project properties.
*/
private void processProjectProperties()
{
//
// Process common attributes
//
List<Row> rows = getRows("project", "proj_id", m_projectID);
m_reader.processProjectProperties(rows);
//
// Process XER-specific attributes
//
if (m_defaultCurrencyData != null)
{
m_reader.processDefaultCurrency(m_defaultCurrencyData);
}
}
/**
* Process user defined fields.
*/
private void processUserDefinedFields()
{
List<Row> udfs = getRows("udftype", null, null);
m_reader.processUserDefinedFields(udfs);
}
/**
* Process project calendars.
*/
private void processCalendars()
{
List<Row> rows = getRows("calendar", null, null);
m_reader.processCalendars(rows);
}
/**
* Process resources.
*/
private void processResources()
{
List<Row> rows = getRows("rsrc", null, null);
m_reader.processResources(rows);
}
/**
* Process tasks.
*/
private void processTasks()
{
List<Row> wbs = getRows("projwbs", "proj_id", m_projectID);
List<Row> tasks = getRows("task", "proj_id", m_projectID);
List<Row> costs = getRows("projcost", "proj_id", m_projectID);
//List<Row> wbsmemos = getRows("wbsmemo", "proj_id", m_projectID);
//List<Row> taskmemos = getRows("taskmemo", "proj_id", m_projectID);
List<Row> udfVals = getRows("udfvalue", "proj_id", m_projectID);
Collections.sort(wbs, WBS_ROW_COMPARATOR);
m_reader.processTasks(wbs, tasks, costs, udfVals/*, wbsmemos, taskmemos*/);
}
/**
* Process predecessors.
*/
private void processPredecessors()
{
List<Row> rows = getRows("taskpred", "proj_id", m_projectID);
m_reader.processPredecessors(rows);
}
/**
* Process resource assignments.
*/
private void processAssignments()
{
List<Row> rows = getRows("taskrsrc", "proj_id", m_projectID);
m_reader.processAssignments(rows);
}
/**
* Reads each token from a single record and adds it to a list.
*
* @param tk tokenizer
* @param record list of tokens
* @throws IOException
*/
private void readRecord(Tokenizer tk, List<String> record) throws IOException
{
record.clear();
while (tk.nextToken() == Tokenizer.TT_WORD)
{
record.add(tk.getToken());
}
}
/**
* Handles a complete record at a time, stores it in a form ready for
* further processing.
*
* @param record record to be processed
* @return flag indicating if this is the last record in the file to be processed
* @throws MPXJException
*/
private boolean processRecord(List<String> record) throws MPXJException
{
boolean done = false;
XerRecordType type = RECORD_TYPE_MAP.get(record.get(0));
if (type == null)
{
throw new MPXJException(MPXJException.INVALID_FORMAT);
}
switch (type)
{
case HEADER:
{
processHeader(record);
break;
}
case TABLE:
{
m_currentTableName = record.get(1).toLowerCase();
m_skipTable = !REQUIRED_TABLES.contains(m_currentTableName);
if (m_skipTable)
{
m_currentTable = null;
}
else
{
m_currentTable = new LinkedList<Row>();
m_tables.put(m_currentTableName, m_currentTable);
}
break;
}
case FIELDS:
{
if (m_skipTable)
{
m_currentFieldNames = null;
}
else
{
m_currentFieldNames = record.toArray(new String[record.size()]);
for (int loop = 0; loop < m_currentFieldNames.length; loop++)
{
m_currentFieldNames[loop] = m_currentFieldNames[loop].toLowerCase();
}
}
break;
}
case DATA:
{
if (!m_skipTable)
{
Map<String, Object> map = new HashMap<String, Object>();
for (int loop = 1; loop < record.size(); loop++)
{
String fieldName = m_currentFieldNames[loop];
String fieldValue = record.get(loop);
XerFieldType fieldType = FIELD_TYPE_MAP.get(fieldName);
if (fieldType == null)
{
fieldType = XerFieldType.STRING;
}
Object objectValue;
if (fieldValue.length() == 0)
{
objectValue = null;
}
else
{
switch (fieldType)
{
case DATE:
{
try
{
objectValue = m_df.parseObject(fieldValue);
}
catch (ParseException ex)
{
throw new MPXJException(MPXJException.INVALID_DATE, ex);
}
break;
}
case CURRENCY:
{
try
{
objectValue = Double.valueOf(m_numberFormat.parse(fieldValue).doubleValue());
}
catch (ParseException ex)
{
throw new MPXJException(MPXJException.INVALID_NUMBER, ex);
}
break;
}
case DOUBLE:
{
try
{
objectValue = Double.valueOf(m_numberFormat.parse(fieldValue).doubleValue());
}
catch (ParseException ex)
{
throw new MPXJException(MPXJException.INVALID_NUMBER, ex);
}
break;
}
case DURATION:
{
try
{
objectValue = Double.valueOf(m_numberFormat.parse(fieldValue).doubleValue());
}
catch (ParseException ex)
{
throw new MPXJException(MPXJException.INVALID_NUMBER, ex);
}
break;
}
case INTEGER:
{
objectValue = Integer.valueOf(fieldValue);
break;
}
default:
{
objectValue = fieldValue;
break;
}
}
}
map.put(fieldName, objectValue);
}
Row currentRow = new MapRow(map);
m_currentTable.add(currentRow);
//
// Special case - we need to know the default currency format
// ahead of time, so process each row as we get it so that
// we can correctly parse currency values in later tables.
//
if (m_currentTableName.equals("currtype"))
{
processCurrency(currentRow);
}
}
break;
}
case END:
{
done = true;
break;
}
default:
{
break;
}
}
return done;
}
/**
* Extract any useful attributes from the header record.
*
* @param record header record
*/
private void processHeader(List<String> record)
{
m_defaultCurrencyName = record.size() > 8 ? record.get(8) : "USD";
}
/**
* Override the default field name mapping for user defined types.
*
* @param type target user defined data type
* @param fieldName field name
*/
public void setFieldNameForUdfType(UserFieldDataType type, String fieldName)
{
m_udfCounters.setFieldNameForType(type, fieldName);
}
/**
* Customise the data retrieved by this reader by modifying the contents of this map.
*
* @return Primavera field name to MPXJ field type map
*/
public Map<FieldType, String> getResourceFieldMap()
{
return m_resourceFields;
}
/**
* Customise the data retrieved by this reader by modifying the contents of this map.
*
* @return Primavera field name to MPXJ field type map
*/
public Map<FieldType, String> getWbsFieldMap()
{
return m_wbsFields;
}
/**
* Customise the data retrieved by this reader by modifying the contents of this map.
*
* @return Primavera field name to MPXJ field type map
*/
public Map<FieldType, String> getTaskFieldMap()
{
return m_taskFields;
}
/**
* Customise the data retrieved by this reader by modifying the contents of this map.
*
* @return Primavera field name to MPXJ field type map
*/
public Map<FieldType, String> getAssignmentFields()
{
return m_assignmentFields;
}
/**
* Customise the MPXJ field name aliases applied by this reader by modifying the contents of this map.
*
* @return Primavera field name to MPXJ field type map
*/
public Map<FieldType, String> getAliases()
{
return m_aliases;
}
/**
* Filters a list of rows from the named table. If a column name and a value
* are supplied, then use this to filter the rows. If no column name is
* supplied, then return all rows.
*
* @param tableName table name
* @param columnName filter column name
* @param id filter column value
* @return filtered list of rows
*/
private List<Row> getRows(String tableName, String columnName, Integer id)
{
List<Row> result;
List<Row> table = m_tables.get(tableName);
if (table == null)
{
result = EMPTY_TABLE;
}
else
{
if (columnName == null)
{
result = table;
}
else
{
result = new LinkedList<Row>();
for (Row row : table)
{
if (NumberHelper.equals(id, row.getInteger(columnName)))
{
result.add(row);
}
}
}
}
return result;
}
/**
* If set to true, the WBS for each task read from Primavera will exactly match the WBS value shown in Primavera.
* If set to false, each task will be given a unique WBS based on the WBS present in Primavera.
* Defaults to true.
*
* @return flag value
*/
public boolean getMatchPrimaveraWBS()
{
return m_matchPrimaveraWBS;
}
/**
* If set to true, the WBS for each task read from Primavera will exactly match the WBS value shown in Primavera.
* If set to false, each task will be given a unique WBS based on the WBS present in Primavera.
* Defaults to true.
*
* @param matchPrimaveraWBS flag value
*/
public void setMatchPrimaveraWBS(boolean matchPrimaveraWBS)
{
m_matchPrimaveraWBS = matchPrimaveraWBS;
}
private PrimaveraReader m_reader;
private Integer m_projectID;
boolean m_skipTable;
private Map<String, List<Row>> m_tables;
private String m_currentTableName;
private List<Row> m_currentTable;
private String[] m_currentFieldNames;
private String m_defaultCurrencyName;
private Map<String, DecimalFormat> m_currencyMap = new HashMap<String, DecimalFormat>();
private DecimalFormat m_numberFormat;
private Row m_defaultCurrencyData;
private DateFormat m_df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
private static final List<Row> EMPTY_TABLE = new LinkedList<Row>();
private List<ProjectListener> m_projectListeners;
private UserFieldCounters m_udfCounters = new UserFieldCounters();
private Map<FieldType, String> m_resourceFields = PrimaveraReader.getDefaultResourceFieldMap();
private Map<FieldType, String> m_wbsFields = PrimaveraReader.getDefaultWbsFieldMap();
private Map<FieldType, String> m_taskFields = PrimaveraReader.getDefaultTaskFieldMap();
private Map<FieldType, String> m_assignmentFields = PrimaveraReader.getDefaultAssignmentFieldMap();
private Map<FieldType, String> m_aliases = PrimaveraReader.getDefaultAliases();
private boolean m_matchPrimaveraWBS = true;
/**
* Represents expected record types.
*/
private enum XerRecordType
{
HEADER,
TABLE,
FIELDS,
DATA,
END
}
/**
* Represents column data types.
*/
private enum XerFieldType
{
STRING,
INTEGER,
DOUBLE,
DATE,
DURATION,
CURRENCY
}
/**
* Maps record type text to record types.
*/
private static final Map<String, XerRecordType> RECORD_TYPE_MAP = new HashMap<String, XerRecordType>();
static
{
RECORD_TYPE_MAP.put("RMHDR", XerRecordType.HEADER);
RECORD_TYPE_MAP.put("%T", XerRecordType.TABLE);
RECORD_TYPE_MAP.put("%F", XerRecordType.FIELDS);
RECORD_TYPE_MAP.put("%R", XerRecordType.DATA);
RECORD_TYPE_MAP.put("%E", XerRecordType.END);
}
/**
* Maps field names to data types.
*/
private static final Map<String, XerFieldType> FIELD_TYPE_MAP = new HashMap<String, XerFieldType>();
static
{
FIELD_TYPE_MAP.put("proj_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("create_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("plan_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("plan_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("rsrc_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("create_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("wbs_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("orig_cost", XerFieldType.CURRENCY);
FIELD_TYPE_MAP.put("indep_remain_total_cost", XerFieldType.CURRENCY);
FIELD_TYPE_MAP.put("indep_remain_work_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("anticip_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("anticip_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("parent_wbs_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("task_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("phys_complete_pct", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("remain_drtn_hr_cnt", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("act_work_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("remain_work_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("target_work_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("target_drtn_hr_cnt", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("cstr_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("act_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("act_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("late_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("late_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("expect_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("early_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("early_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("target_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("target_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("restart_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("reend_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("create_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("pred_task_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("lag_hr_cnt", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("remain_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("target_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("act_reg_qty", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("target_cost", XerFieldType.CURRENCY);
FIELD_TYPE_MAP.put("act_reg_cost", XerFieldType.CURRENCY);
FIELD_TYPE_MAP.put("target_start_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("target_end_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("act_equip_qty", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("remain_equip_qty", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("clndr_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("default_flag", XerFieldType.STRING);
FIELD_TYPE_MAP.put("clndr_name", XerFieldType.STRING);
FIELD_TYPE_MAP.put("proj_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("base_clndr_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("last_chng_date", XerFieldType.STRING);
FIELD_TYPE_MAP.put("clndr_type", XerFieldType.STRING);
FIELD_TYPE_MAP.put("day_hr_cnt", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("week_hr_cnt", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("month_hr_cnt", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("year_hr_cnt", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("clndr_data", XerFieldType.STRING);
FIELD_TYPE_MAP.put("seq_num", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("taskrsrc_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("parent_rsrc_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("free_float_hr_cnt", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("total_float_hr_cnt", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("decimal_digit_cnt", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("target_qty_per_hr", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("target_lag_drtn_hr_cnt", XerFieldType.DURATION);
FIELD_TYPE_MAP.put("act_cost", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("target_cost", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("remain_cost", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("last_recalc_date", XerFieldType.DATE);
// User Defined Fields types (UDF)
FIELD_TYPE_MAP.put("udf_type", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("table_name", XerFieldType.STRING);
FIELD_TYPE_MAP.put("udf_type_name", XerFieldType.STRING);
FIELD_TYPE_MAP.put("udf_type_label", XerFieldType.STRING);
FIELD_TYPE_MAP.put("loginal_data_type", XerFieldType.STRING);
FIELD_TYPE_MAP.put("super_flag", XerFieldType.STRING);
// User Defined Fields values
FIELD_TYPE_MAP.put("fk_id", XerFieldType.INTEGER);
FIELD_TYPE_MAP.put("udf_date", XerFieldType.DATE);
FIELD_TYPE_MAP.put("udf_number", XerFieldType.DOUBLE);
FIELD_TYPE_MAP.put("udf_text", XerFieldType.STRING);
FIELD_TYPE_MAP.put("udf_code_id", XerFieldType.INTEGER);
}
private static final Set<String> REQUIRED_TABLES = new HashSet<String>();
static
{
REQUIRED_TABLES.add("project");
REQUIRED_TABLES.add("calendar");
REQUIRED_TABLES.add("rsrc");
REQUIRED_TABLES.add("projwbs");
REQUIRED_TABLES.add("task");
REQUIRED_TABLES.add("taskpred");
REQUIRED_TABLES.add("taskrsrc");
REQUIRED_TABLES.add("currtype");
REQUIRED_TABLES.add("udftype");
REQUIRED_TABLES.add("udfvalue");
REQUIRED_TABLES.add("projcost");
}
private static final WbsRowComparator WBS_ROW_COMPARATOR = new WbsRowComparator();
}