Skip to content

Commit

Permalink
Link Cross Project Relations Populate Predecessors and Successors (#769)
Browse files Browse the repository at this point in the history
  • Loading branch information
joniles authored Nov 6, 2024
1 parent abd3462 commit f9f0020
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<action dev="joniles" type="update">Improve handling of unexpected data types when writing JSON files.</action>
<action dev="joniles" type="update">Added the `Relation.getPredecessorTask()` and `Relation.getSuccessorTask()` methods.</action>
<action dev="joniles" type="update">Marked the `Relation.getSourceTask()` and `Relation.getTargetTask()` methods as deprecated, use the `Relation.getPredecessorTask()` and `Relation.getSuccessorTask()` instead.</action>
<action dev="joniles" type="update">Ensure that with "Link Cross Project Relations" enabled when reading XER or PMXML files, the predecessor and successor lists for both tasks related acrosss projects are correctly populated.</action>
</release>
<release date="2024-10-28" version="13.5.1">
<action dev="joniles" type="update">Fix CVE-2024-49771: Potential Path Traversal Vulnerability (Contributed by yyjLF and sprinkle).</action>
Expand Down
58 changes: 46 additions & 12 deletions src/main/java/net/sf/mpxj/explorer/ProjectExplorer.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@

import net.sf.mpxj.MPXJException;
import net.sf.mpxj.ProjectFile;
import net.sf.mpxj.primavera.PrimaveraPMFileReader;
import net.sf.mpxj.primavera.PrimaveraXERFileReader;
import net.sf.mpxj.reader.ProjectReader;
import net.sf.mpxj.reader.UniversalProjectReader;

/**
Expand All @@ -60,6 +63,7 @@ public class ProjectExplorer

protected JFrame m_frame;
private boolean m_openAll;
private boolean m_linkCrossProjectRelations;
private boolean m_expandSubprojects;
private boolean m_removeExternalTasks = true;
private final JMenuItem m_saveMenu = new JMenuItem("Save As...");
Expand Down Expand Up @@ -164,6 +168,9 @@ private void initialize()
final JMenuItem mntmOpenAll = new JCheckBoxMenuItem("Open All");
mnFile.add(mntmOpenAll);

final JMenuItem mntmLinkCrossProjectRelations = new JCheckBoxMenuItem("Link Cross Project Relations", m_linkCrossProjectRelations);
mnFile.add(mntmLinkCrossProjectRelations);

final JMenuItem mntmExpandSubprojects = new JCheckBoxMenuItem("Expand Subprojects", m_expandSubprojects);
mnFile.add(mntmExpandSubprojects);

Expand Down Expand Up @@ -200,6 +207,11 @@ private void initialize()
//
mntmOpenAll.addActionListener(e -> m_openAll = !m_openAll);

//
// Link Cross Project Relations
//
mntmLinkCrossProjectRelations.addActionListener(e -> m_linkCrossProjectRelations = !m_linkCrossProjectRelations);

//
// Expand Subprojects
//
Expand Down Expand Up @@ -238,42 +250,45 @@ private void initialize()

private void openFile(File file)
{
try
{
updateAndSaveRecents(file);
updateAndSaveRecents(file);

ProjectFile projectFile = new UniversalProjectReader().read(file);
if (projectFile == null)
try (UniversalProjectReader.ProjectReaderProxy proxy = new UniversalProjectReader().getProjectReaderProxy(file))
{
if (proxy == null)
{
JOptionPane.showMessageDialog(m_frame, "Unsupported file type");
return;
}

configureReader(proxy);
ProjectFile projectFile = proxy.read();
expandSubprojects(file, projectFile);
m_tabbedPane.add(file.getName(), new ProjectFilePanel(file, projectFile));
m_saveMenu.setEnabled(true);
m_cleanMenu.setEnabled(true);
}

catch (MPXJException ex)
catch (Exception ex)
{
throw new IllegalArgumentException("Failed to read file", ex);
}
}

private void openAll(File file)
{
try
{
updateAndSaveRecents(file);
updateAndSaveRecents(file);

List<ProjectFile> projectFiles = new UniversalProjectReader().readAll(file);
if (projectFiles.isEmpty())
try (UniversalProjectReader.ProjectReaderProxy proxy = new UniversalProjectReader().getProjectReaderProxy(file);)
{
if (proxy == null)
{
JOptionPane.showMessageDialog(m_frame, "Unsupported file type");
return;
}

configureReader(proxy);
List<ProjectFile> projectFiles = proxy.readAll();

int index = 1;
for (ProjectFile projectFile : projectFiles)
{
Expand All @@ -285,7 +300,7 @@ private void openAll(File file)
m_cleanMenu.setEnabled(true);
}

catch (MPXJException ex)
catch (Exception ex)
{
throw new IllegalArgumentException("Failed to read file", ex);
}
Expand Down Expand Up @@ -451,6 +466,25 @@ private void expandSubprojects(File file, ProjectFile projectFile)
}
}

/**
* Apply any reader-specific configuration.
*
* @param proxy project reader proxy
*/
private void configureReader(UniversalProjectReader.ProjectReaderProxy proxy)
{
ProjectReader reader = proxy.getProjectReader();
if (reader instanceof PrimaveraXERFileReader)
{
((PrimaveraXERFileReader)reader).setLinkCrossProjectRelations(m_linkCrossProjectRelations);
}

if (reader instanceof PrimaveraPMFileReader)
{
((PrimaveraPMFileReader)reader).setLinkCrossProjectRelations(m_linkCrossProjectRelations);
}
}

private static final String[] READ_EXTENSIONS =
{
"bk3",
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/net/sf/mpxj/primavera/PrimaveraPMFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,23 @@ private void linkCrossProjectRelations(List<ProjectFile> projects)
predecessor = externalRelation.getTargetTask();
}

successor.addPredecessor(new Relation.Builder()
// We need to ensure that the relation is present in both
// projects so that predecessors and successors are populated
// in both projects.

ProjectFile successorProject = successor.getParentFile();
successorProject.getRelations().addPredecessor(new Relation.Builder()
.predecessorTask(predecessor)
.successorTask(successor)
.type(externalRelation.getType())
.lag(externalRelation.getLag())
.uniqueID(externalRelation.getUniqueID())
.notes(externalRelation.getNotes()));

ProjectFile predecessorProject = predecessor.getParentFile();
predecessorProject.getRelations().addPredecessor(new Relation.Builder()
.predecessorTask(predecessor)
.successorTask(successor)
.type(externalRelation.getType())
.lag(externalRelation.getLag())
.uniqueID(externalRelation.getUniqueID())
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/net/sf/mpxj/primavera/PrimaveraXERFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,30 @@ public void setLinkCrossProjectRelations(boolean linkCrossProjectRelations)
predecessorTask = proj.getTaskByUniqueID(externalRelation.externalTaskUniqueID());
if (predecessorTask != null)
{
externalRelation.getTargetTask().addPredecessor(new Relation.Builder()
Task successorTask = externalRelation.getTargetTask();

// We need to ensure that the relation is present in both
// projects so that predecessors and successors are populated
// in both projects.

ProjectFile successorProject = successorTask.getParentFile();
successorProject.getRelations().addPredecessor(new Relation.Builder()
.predecessorTask(predecessorTask)
.successorTask(successorTask)
.type(externalRelation.getType())
.lag(externalRelation.getLag())
.uniqueID(externalRelation.getUniqueID())
.notes(externalRelation.getNotes()));

ProjectFile predecessorProject = predecessorTask.getParentFile();
predecessorProject.getRelations().addPredecessor(new Relation.Builder()
.predecessorTask(predecessorTask)
.successorTask(successorTask)
.type(externalRelation.getType())
.lag(externalRelation.getLag())
.uniqueID(externalRelation.getUniqueID())
.notes(externalRelation.getNotes()));

break;
}
}
Expand Down

0 comments on commit f9f0020

Please sign in to comment.