diff --git a/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java b/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java
index b17eda7d1..b8cd99106 100644
--- a/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java
+++ b/src/main/java/org/mastodon/mamut/classification/ClassifyLineagesController.java
@@ -28,13 +28,13 @@
*/
package org.mastodon.mamut.classification;
-import mpicbg.spim.data.SpimDataException;
import org.apache.commons.lang3.tuple.Pair;
import org.mastodon.collection.RefSet;
import org.mastodon.graph.algorithm.traversal.DepthFirstIterator;
import org.mastodon.mamut.ProjectModel;
import org.mastodon.mamut.classification.config.ClusteringMethod;
import org.mastodon.mamut.classification.config.SimilarityMeasure;
+import org.mastodon.mamut.classification.multiproject.ExternalProjects;
import org.mastodon.mamut.classification.util.Classification;
import org.mastodon.mamut.classification.config.CropCriteria;
import org.mastodon.mamut.classification.ui.DendrogramView;
@@ -60,12 +60,9 @@
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -90,8 +87,6 @@ public class ClassifyLineagesController
private final PrefService prefs;
- private final MastodonProjectService projectService;
-
private SimilarityMeasure similarityMeasure = SimilarityMeasure.NORMALIZED_ZHANG_DIFFERENCE;
private ClusteringMethod clusteringMethod = ClusteringMethod.AVERAGE_LINKAGE;
@@ -106,12 +101,10 @@ public class ClassifyLineagesController
private int minCellDivisions;
- private final Map< File, ProjectSession > externalProjects;
-
- private final Map< File, String > failingExternalProjects;
-
private boolean showDendrogram;
+ private final ExternalProjects externalProjects;
+
private boolean addTagSetToExternalProjects;
private boolean running = false;
@@ -137,9 +130,7 @@ public ClassifyLineagesController( final ProjectModel referenceProjectModel, fin
this.referenceProjectModel = referenceProjectModel;
this.referenceModel = referenceProjectModel.getModel();
this.prefs = prefs;
- this.projectService = projectService;
- this.externalProjects = new HashMap<>();
- this.failingExternalProjects = new HashMap<>();
+ this.externalProjects = new ExternalProjects( projectService );
}
/**
@@ -197,10 +188,10 @@ private Classification< BranchSpotTree > classifyUsingExternalProjects( final Li
for ( int i = 1; i < roots.size(); i++ )
{
averageClassification = classifyLineageTrees( roots.get( i ), distances );
- for ( ProjectSession projectSession : externalProjects.values() )
+ for ( Map.Entry< File, ProjectSession > externalProject : externalProjects.getProjects().entrySet() )
{
- ProjectModel projectModel = projectSession.getProjectModel();
- File file = projectSession.getFile();
+ ProjectModel projectModel = externalProject.getValue().getProjectModel();
+ File file = externalProject.getKey();
branchSpotProvider = branchSpotTree -> projectModel.getModel().getBranchGraph().vertices().stream()
.filter( ( branchSpot -> branchSpot.getFirstLabel().equals( branchSpotTree.getName() ) ) )
.findFirst().orElse( null );
@@ -233,9 +224,9 @@ private Pair< List< List< BranchSpotTree > >, double[][] > getRootsAndDistanceMa
keepCommonRootsAndSort( roots, commonRootNames );
treeMatrix.add( roots );
- for ( ProjectSession projectSession : externalProjects.values() )
+ for ( ProjectModel projectModel : externalProjects.getProjectModels() )
{
- List< BranchSpotTree > externalRoots = getRoots( projectSession.getProjectModel() );
+ List< BranchSpotTree > externalRoots = getRoots( projectModel );
keepCommonRootsAndSort( externalRoots, commonRootNames );
treeMatrix.add( externalRoots );
}
@@ -245,9 +236,9 @@ private Pair< List< List< BranchSpotTree > >, double[][] > getRootsAndDistanceMa
private List< String > findCommonRootNames()
{
Set< String > commonRootNames = extractRootNamesFromProjectModel( referenceProjectModel );
- for ( ProjectSession projectSession : externalProjects.values() )
+ for ( ProjectModel projectModel : externalProjects.getProjectModels() )
{
- Set< String > rootNames = extractRootNamesFromProjectModel( projectSession.getProjectModel() );
+ Set< String > rootNames = extractRootNamesFromProjectModel( projectModel );
commonRootNames.retainAll( rootNames );
}
List< String > commonRootNamesList = new ArrayList<>( commonRootNames );
@@ -430,68 +421,7 @@ public void setShowDendrogram( final boolean showDendrogram )
public void setExternalProjects( final File[] projects, final boolean addTagSetToExternalProjects )
{
this.addTagSetToExternalProjects = addTagSetToExternalProjects;
- List< File > projectsList = projects == null ? Collections.emptyList() : Arrays.asList( projects );
- removeProjects( projectsList );
- cleanUpFailingProjects( projectsList );
- addProjects( projects );
- }
-
- /**
- * Remove files from the externalProjects map that are not in the projects list
- */
- private void removeProjects( final List< File > projectsList )
- {
- Iterator< Map.Entry< File, ProjectSession > > iterator = externalProjects.entrySet().iterator();
- while ( iterator.hasNext() )
- {
- Map.Entry< File, ProjectSession > entry = iterator.next();
- File file = entry.getKey();
- if ( !projectsList.contains( file ) )
- {
- ProjectSession projectSession = entry.getValue();
- projectSession.close();
- iterator.remove();
- }
- }
- }
-
- /**
- * Remove files from the failingExternalProjects map that are not in the projects list
- */
- private void cleanUpFailingProjects( final List< File > projectsList )
- {
- for ( Map.Entry< File, String > entry : failingExternalProjects.entrySet() )
- {
- File file = entry.getKey();
- if ( !projectsList.contains( file ) )
- failingExternalProjects.remove( file );
- }
- }
-
- /**
- * Add files from projects to the map if they are not already present
- */
- private void addProjects( final File[] projects )
- {
- if ( projects == null )
- return;
- for ( File file : projects )
- {
- if ( !externalProjects.containsKey( file ) )
- {
- try
- {
- externalProjects.put( file, projectService.createSession( file ) );
- failingExternalProjects.remove( file );
- }
- catch ( SpimDataException | IOException | RuntimeException e )
- {
- failingExternalProjects.put( file,
- "Could not read project from file " + file.getAbsolutePath() + ".
Error: " + e.getMessage() );
- logger.warn( "Could not read project from file {}. Error: {}", file.getAbsolutePath(), e.getMessage() );
- }
- }
- }
+ externalProjects.setProjects( projects );
}
public List< String > getFeedback()
@@ -514,7 +444,7 @@ public List< String > getFeedback()
}
if ( cropCriterion.equals( CropCriteria.NUMBER_OF_SPOTS ) )
feedback.addAll( checkNumberOfSpots() );
- feedback.addAll( failingExternalProjects.values() );
+ feedback.addAll( externalProjects.getFailingProjectMessages() );
return feedback;
}
@@ -544,8 +474,7 @@ private List< String > checkNumberOfSpots()
{
List< String > feedback = new ArrayList<>();
Set< ProjectModel > allModels = new HashSet<>( Collections.singletonList( referenceProjectModel ) );
- for ( ProjectSession projectSession : externalProjects.values() )
- allModels.add( projectSession.getProjectModel() );
+ allModels.addAll( externalProjects.getProjectModels() );
for ( ProjectModel projectModel : allModels )
{
Model model = projectModel.getModel();
@@ -576,7 +505,6 @@ private List< String > checkNumberOfSpots()
public void close()
{
- for ( ProjectSession projectSession : externalProjects.values() )
- projectSession.close();
+ externalProjects.close();
}
}
diff --git a/src/main/java/org/mastodon/mamut/classification/multiproject/ExternalProjects.java b/src/main/java/org/mastodon/mamut/classification/multiproject/ExternalProjects.java
new file mode 100644
index 000000000..d9098075e
--- /dev/null
+++ b/src/main/java/org/mastodon/mamut/classification/multiproject/ExternalProjects.java
@@ -0,0 +1,167 @@
+package org.mastodon.mamut.classification.multiproject;
+
+import mpicbg.spim.data.SpimDataException;
+import org.mastodon.mamut.ProjectModel;
+import org.mastodon.mamut.util.MastodonProjectService;
+import org.mastodon.mamut.util.ProjectSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * A collection of external projects.
+ *
+ * This class manages a collection of external projects. It holds a mapping of project files to project sessions and a mapping of projects that failed to be loaded and the reason why they failed to load.
+ */
+public class ExternalProjects implements AutoCloseable
+{
+ private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );
+
+ private final MastodonProjectService projectService;
+
+ private final Map< File, ProjectSession > projectSessions;
+
+ private final Map< File, String > failingProjects;
+
+ public ExternalProjects( final MastodonProjectService projectService )
+ {
+ this.projectService = projectService;
+ this.projectSessions = new HashMap<>();
+ this.failingProjects = new HashMap<>();
+ }
+
+ /**
+ * Gets all project models of the external projects
+ * @return the project models
+ */
+ public Collection< ProjectModel > getProjectModels()
+ {
+ return projectSessions.values().stream().map( ProjectSession::getProjectModel ).collect( Collectors.toList() );
+ }
+
+ /**
+ * Gets a mapping of external project files to their project sessions
+ * @return the mapping
+ */
+ public Map< File, ProjectSession > getProjects()
+ {
+ return projectSessions;
+ }
+
+ /**
+ * Get the number of external projects
+ * @return the number of external projects
+ */
+ public int size()
+ {
+ return projectSessions.size();
+ }
+
+ /**
+ * Check if the external projects is empty
+ * @return {@code true} if the external projects is empty, {@code false} otherwise
+ */
+ public boolean isEmpty()
+ {
+ return projectSessions.isEmpty();
+ }
+
+ /**
+ * Get a Collection failing projects and the reason why they failed to load.
+ * @return the failing projects
+ */
+ public Collection< String > getFailingProjectMessages()
+ {
+ return failingProjects.values();
+ }
+
+ /**
+ * Set the external projects
+ * @param projects the external projects
+ */
+ public void setProjects( final File[] projects )
+ {
+ List< File > projectsList = projects == null ? Collections.emptyList() : Arrays.asList( projects );
+ removeProjects( projectsList );
+ cleanUpFailingProjects( projectsList );
+ addProjects( projects );
+ logger.debug( "Set {} projects. Active project sessions: {}.", projectsList.size(), projectService.activeSessions() );
+ }
+
+ /**
+ * Remove files from the externalProjects map that are not in the projects list
+ */
+ private void removeProjects( final List< File > projectsList )
+ {
+ Iterator< Map.Entry< File, ProjectSession > > iterator = projectSessions.entrySet().iterator();
+ while ( iterator.hasNext() )
+ {
+ Map.Entry< File, ProjectSession > entry = iterator.next();
+ File file = entry.getKey();
+ if ( !projectsList.contains( file ) )
+ {
+ ProjectSession projectSession = entry.getValue();
+ projectSession.close();
+ iterator.remove();
+ }
+ }
+ }
+
+ /**
+ * Remove files from the failingExternalProjects map that are not in the projects list
+ */
+ private void cleanUpFailingProjects( final List< File > projectsList )
+ {
+ for ( Map.Entry< File, String > entry : failingProjects.entrySet() )
+ {
+ File file = entry.getKey();
+ if ( !projectsList.contains( file ) )
+ failingProjects.remove( file );
+ }
+ }
+
+ /**
+ * Add files from projects to the map if they are not already present
+ */
+ private void addProjects( final File[] files )
+ {
+ if ( files == null )
+ return;
+ for ( File file : files )
+ {
+ if ( !projectSessions.containsKey( file ) )
+ {
+ try
+ {
+ projectSessions.put( file, projectService.createSession( file ) );
+ failingProjects.remove( file );
+ }
+ catch ( SpimDataException | IOException | RuntimeException e )
+ {
+ failingProjects.put( file,
+ "Could not read project from file " + file.getAbsolutePath() + ".
Error: " + e.getMessage() );
+ logger.warn( "Could not read project from file {}. Error: {}", file.getAbsolutePath(), e.getMessage() );
+ }
+ }
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ for ( ProjectSession projectSession : projectSessions.values() )
+ projectSession.close();
+ logger.debug( "Remaining active project sessions: {}.", projectService.activeSessions() );
+ }
+}
diff --git a/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java b/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java
index d806acbe2..1899a1748 100644
--- a/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java
+++ b/src/main/java/org/mastodon/mamut/util/MastodonProjectService.java
@@ -68,7 +68,7 @@ public ProjectSession createSession( final File file ) throws SpimDataException,
if ( projectModels.containsKey( file ) )
{
ProjectModel projectModel = projectModels.get( file );
- ProjectSession projectSession = new ProjectSession( file, projectModel, this );
+ ProjectSession projectSession = new ProjectSession( projectModel, this );
sessions.get( projectModel ).add( projectSession );
return projectSession;
}
@@ -78,7 +78,7 @@ public ProjectSession createSession( final File file ) throws SpimDataException,
ProjectModel projectModel =
ProjectLoader.open( file.getAbsolutePath(), getContext(), false, true );
logger.debug( "Loaded project from file: {} in {} ms", file.getAbsolutePath(), System.currentTimeMillis() - start );
- ProjectSession projectSession = new ProjectSession( file, projectModel, this );
+ ProjectSession projectSession = new ProjectSession( projectModel, this );
projectModels.put( file, projectModel );
List< ProjectSession > projectSessionList = new ArrayList<>();
projectSessionList.add( projectSession );
diff --git a/src/main/java/org/mastodon/mamut/util/ProjectSession.java b/src/main/java/org/mastodon/mamut/util/ProjectSession.java
index cb8af2ccd..2e09c7e31 100644
--- a/src/main/java/org/mastodon/mamut/util/ProjectSession.java
+++ b/src/main/java/org/mastodon/mamut/util/ProjectSession.java
@@ -15,19 +15,15 @@ public class ProjectSession implements AutoCloseable
{
private final MastodonProjectService service;
- private final File file;
-
private final ProjectModel projectModel;
/**
* Creates a new project session.
- * @param file the file the project was loaded from.
* @param projectModel the project model.
* @param service the service that created this session.
*/
- public ProjectSession( final File file, final ProjectModel projectModel, final MastodonProjectService service )
+ public ProjectSession( final ProjectModel projectModel, final MastodonProjectService service )
{
- this.file = file;
this.projectModel = projectModel;
this.service = service;
}
@@ -49,13 +45,4 @@ public ProjectModel getProjectModel()
{
return projectModel;
}
-
- /**
- * Gets the file the project was loaded from.
- * @return the file.
- */
- public File getFile()
- {
- return file;
- }
}
diff --git a/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java b/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java
index 6db12eef6..3a3f64a01 100644
--- a/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java
+++ b/src/test/java/org/mastodon/mamut/util/MastodonProjectServiceTest.java
@@ -39,9 +39,6 @@ void test() throws IOException, SpimDataException
assertEquals( 1, service.activeSessions() );
projectSession3.close();
assertEquals( 0, service.activeSessions() );
- assertEquals( mastodonFile1, projectSession1.getFile() );
- assertEquals( mastodonFile1, projectSession2.getFile() );
- assertEquals( mastodonFile2, projectSession3.getFile() );
}
}
}