net.sf.mpxj.asta.AbstractAstaDatabaseReader 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: AbstractDatabaseReader.java
* author: Jon Iles
* copyright: (c) Packwood Software 2022
* date: 07/07/2022
*/
/*
* 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.asta;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import net.sf.mpxj.DayType;
import net.sf.mpxj.MPXJException;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.common.HierarchyHelper;
import net.sf.mpxj.reader.AbstractProjectFileReader;
/**
* This class provides a generic front end to read project data from
* a database.
*/
abstract class AbstractAstaDatabaseReader extends AbstractProjectFileReader
{
/**
* Populates a Map instance representing the IDs and names of
* projects available in the current database.
*
* @return Map instance containing ID and name pairs
*/
public Map listProjects() throws MPXJException
{
try
{
Map result = new HashMap<>();
List rows = getRows("project_summary", Collections.emptyMap());
for (Row row : rows)
{
Integer id = row.getInteger("PROJID");
String name = row.getString("SHORT_NAME");
result.put(id, name);
}
return result;
}
catch (AstaDatabaseException ex)
{
throw new MPXJException(MPXJException.READ_ERROR, ex);
}
}
/**
* Read a project from the current data source.
*
* @return ProjectFile instance
*/
public ProjectFile read() throws MPXJException
{
try
{
m_reader = new AstaReader();
ProjectFile project = m_reader.getProject();
addListenersToProject(project);
processProjectProperties();
processCalendars();
processResources();
processTasks();
processPredecessors();
processAssignments();
// TODO: user defined field support (where is udf_data?)
project.readComplete();
m_reader = null;
return project;
}
catch (AstaDatabaseException ex)
{
throw new MPXJException(MPXJException.READ_ERROR, ex);
}
finally
{
releaseResources();
}
}
/**
* Select the project properties row from the database.
*/
private void processProjectProperties() throws AstaDatabaseException
{
List schemaVersionRows = getRows("dodschem", Collections.emptyMap());
List projectSummaryRows = getRows("project_summary", m_projectKey);
List progressPeriodRows = getRows("progress_period", m_projectKey);
List userSettingsRows = getRows("userr", m_projectKey);
Integer schemaVersion = schemaVersionRows.isEmpty() ? null : schemaVersionRows.get(0).getInteger("SCHVER");
Row projectSummary = projectSummaryRows.isEmpty() ? null : projectSummaryRows.get(0);
Row userSettings = userSettingsRows.isEmpty() ? null : userSettingsRows.get(0);
List progressPeriods = progressPeriodRows.isEmpty() ? null : progressPeriodRows;
m_reader.processProjectProperties(schemaVersion, projectSummary, userSettings, progressPeriods);
}
/**
* Process calendars.
*/
private void processCalendars() throws AstaDatabaseException
{
List rows = getRows("exceptionn", Collections.emptyMap());
Map exceptionMap = m_reader.createExceptionTypeMap(rows);
rows = getRows("work_pattern", Collections.emptyMap());
Map workPatternMap = m_reader.createWorkPatternMap(rows);
rows = getRows("work_pattern_assignment", Collections.emptyMap());
Map> workPatternAssignmentMap = m_reader.createWorkPatternAssignmentMap(rows);
rows = sortRows(getRows("exception_assignment", Collections.emptyMap()), "EXCEPTION_ASSIGNMENT_ID", "ORDF");
Map> exceptionAssignmentMap = m_reader.createExceptionAssignmentMap(rows);
rows = sortRows(getRows("time_entry", Collections.emptyMap()), "TIME_ENTRYID", "ORDF");
Map> timeEntryMap = m_reader.createTimeEntryMap(rows);
rows = getRows("calendar", m_projectKey);
rows = HierarchyHelper.sortHierarchy(rows, r -> r.getInteger("CALENDARID"), r -> r.getInteger("CALENDAR"));
for (Row row : rows)
{
m_reader.processCalendar(row, workPatternMap, workPatternAssignmentMap, exceptionAssignmentMap, timeEntryMap, exceptionMap);
}
}
/**
* Process resources.
*/
private void processResources() throws AstaDatabaseException
{
List permanentRows = sortRows(getRows("permanent_resource", m_projectKey), "PERMANENT_RESOURCEID");
List consumableRows = sortRows(getRows("consumable_resource", m_projectKey), "CONSUMABLE_RESOURCEID");
m_reader.processResources(permanentRows, consumableRows);
}
/**
* Process tasks.
*/
private void processTasks() throws AstaDatabaseException
{
List bars = getRows("bar", m_projectKey);
List expandedTasks = getRows("expanded_task", m_projectKey);
List tasks = getRows("task", m_projectKey);
List milestones = getRows("milestone", m_projectKey);
List hammocks = getRows("hammock_task", m_projectKey);
m_reader.processTasks(bars, expandedTasks, tasks, milestones, hammocks);
}
/**
* Process predecessors.
*/
private void processPredecessors() throws AstaDatabaseException
{
List rows = sortRows(getRows("link", m_projectKey), "LINKID");
List completedSections = getRows("task_completed_section", m_projectKey);
m_reader.processPredecessors(rows, completedSections);
}
/**
* Process resource assignments.
*/
private void processAssignments() throws AstaDatabaseException
{
List allocationRows = getRows("permanent_schedul_allocation", m_projectKey);
List skillRows = getRows("perm_resource_skill", m_projectKey);
List permanentAssignments = sortRows(joinRows(allocationRows, "ALLOCATIOP_OF", "PERM_RESOURCE_SKILL", skillRows, "PERM_RESOURCE_SKILLID"), "PERMANENT_SCHEDUL_ALLOCATIONID");
m_reader.processAssignments(permanentAssignments);
}
/**
* Set the ID of the project to be read.
*
* @param projectID project ID
*/
public void setProjectID(int projectID)
{
m_projectKey = Collections.singletonMap("PROJID", Integer.valueOf(projectID));
}
@Override public ProjectFile read(File file) throws MPXJException
{
try
{
allocateResources(file);
setProjectID(0);
return read();
}
catch (AstaDatabaseException ex)
{
throw new MPXJException(MPXJException.READ_ERROR, ex);
}
finally
{
releaseResources();
}
}
@Override public List readAll(File file) throws MPXJException
{
try
{
allocateResources(file);
List result = new ArrayList<>();
Set ids = listProjects().keySet();
for (Integer id : ids)
{
setProjectID(id.intValue());
result.add(read());
}
return result;
}
catch (AstaDatabaseException ex)
{
throw new MPXJException(MPXJException.READ_ERROR, ex);
}
finally
{
releaseResources();
}
}
/**
* Retrieve a set of rows from a named table matching the supplied keys.
*
* @param table table to retrieve rows from
* @param keys name and integer value keys
* @return list of rows
*/
protected abstract List getRows(String table, Map keys) throws AstaDatabaseException;
/**
* Allocate any resources necessary to work with the database before we start reading.
*
* @param file database file
*/
protected abstract void allocateResources(File file) throws AstaDatabaseException;
/**
* Release any resources once we've finished reading.
*/
protected abstract void releaseResources();
/**
* Sort rows by the named integer columns.
*
* @param rows list of rows to sort
* @param columnNames columns to sort by
* @return sorted rows
*/
private List sortRows(List rows, String... columnNames)
{
Comparator comparator = Comparator.comparing(r -> Integer.valueOf(r.getInt(columnNames[0])));
if (columnNames.length > 1)
{
for (int index = 1; index < columnNames.length; index++)
{
String columnName = columnNames[index];
comparator = comparator.thenComparing(r -> Integer.valueOf(r.getInt(columnName)));
}
}
rows.sort(comparator);
return rows;
}
/**
* Very basic implementation of an inner join between two result sets.
*
* @param leftRows left result set
* @param leftColumn left foreign key column
* @param rightTable right table name
* @param rightRows right result set
* @param rightColumn right primary key column
* @return joined result set
*/
private List joinRows(List leftRows, String leftColumn, String rightTable, List rightRows, String rightColumn)
{
List result = new ArrayList<>();
RowComparator leftComparator = new RowComparator(leftColumn);
RowComparator rightComparator = new RowComparator(rightColumn);
leftRows.sort(leftComparator);
rightRows.sort(rightComparator);
ListIterator rightIterator = rightRows.listIterator();
Row rightRow = rightIterator.hasNext() ? rightIterator.next() : null;
for (Row leftRow : leftRows)
{
Integer leftValue = leftRow.getInteger(leftColumn);
boolean match = false;
while (rightRow != null)
{
Integer rightValue = rightRow.getInteger(rightColumn);
int comparison = leftValue.compareTo(rightValue);
if (comparison == 0)
{
match = true;
break;
}
if (comparison < 0)
{
if (rightIterator.hasPrevious())
{
rightRow = rightIterator.previous();
}
break;
}
rightRow = rightIterator.next();
}
if (match && rightRow != null)
{
Map newMap = new HashMap<>(((MapRow) leftRow).getMap());
for (Map.Entry entry : ((MapRow) rightRow).getMap().entrySet())
{
String key = entry.getKey();
if (newMap.containsKey(key))
{
key = rightTable + "." + key;
}
newMap.put(key, entry.getValue());
}
result.add(new MapRow(newMap));
}
}
return result;
}
private AstaReader m_reader;
private Map m_projectKey;
}